diff --git a/Gemfile b/Gemfile index a6e27fae..ee78ee85 100644 --- a/Gemfile +++ b/Gemfile @@ -55,6 +55,8 @@ gem 'gaffe' gem 'ruby-filemagic' gem 'mime-types' gem 'midi-smtp-server' +gem 'rswag-api' +gem 'rswag-ui' # we use the git version of acts_as_versioned, and need to include it in this Gemfile gem 'acts_as_versioned', git: 'https://github.com/technoweenie/acts_as_versioned.git' @@ -118,4 +120,5 @@ group :test do # api gem 'apivore', require: false gem 'hashie', '~> 3.4.6', require: false # https://github.com/westfieldlabs/apivore/issues/114 + gem 'rswag-specs' end diff --git a/Gemfile.lock b/Gemfile.lock index c53687fb..7f5e5912 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -430,6 +430,16 @@ GEM rspec-rerun (1.1.0) rspec (~> 3.0) rspec-support (3.11.1) + rswag-api (2.7.0) + railties (>= 3.1, < 7.1) + rswag-specs (2.7.0) + activesupport (>= 3.1, < 7.1) + json-schema (>= 2.2, < 4.0) + railties (>= 3.1, < 7.1) + rspec-core (>= 2.14) + rswag-ui (2.7.0) + actionpack (>= 3.1, < 7.1) + railties (>= 3.1, < 7.1) rubocop (1.36.0) json (~> 2.3) parallel (~> 1.10) @@ -617,6 +627,9 @@ DEPENDENCIES rspec-core rspec-rails rspec-rerun + rswag-api + rswag-specs + rswag-ui rubocop rubocop-rails rubocop-rspec diff --git a/config/initializers/rswag_api.rb b/config/initializers/rswag_api.rb new file mode 100644 index 00000000..4d72f687 --- /dev/null +++ b/config/initializers/rswag_api.rb @@ -0,0 +1,14 @@ +Rswag::Api.configure do |c| + + # 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 rswag-specs to generate Swagger, you'll need to ensure + # that it's configured to generate files in the same folder + c.swagger_root = Rails.root.to_s + '/swagger' + + # Inject a lambda 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/config/initializers/rswag_ui.rb b/config/initializers/rswag_ui.rb new file mode 100644 index 00000000..0a768c17 --- /dev/null +++ b/config/initializers/rswag_ui.rb @@ -0,0 +1,16 @@ +Rswag::Ui.configure do |c| + + # List the Swagger endpoints that you want to be documented through the + # swagger-ui. The first parameter is the path (absolute or relative to the UI + # host) to the corresponding endpoint and the second is a title that will be + # displayed in the document selector. + # NOTE: If you're using rspec-api to expose Swagger files + # (under swagger_root) as JSON or YAML endpoints, then the list below should + # correspond to the relative paths for those endpoints. + + c.swagger_endpoint '/api-docs/v1/swagger.yaml', 'API V1 Docs' + + # Add Basic Auth in case your API is private + # c.basic_auth_enabled = true + # c.basic_auth_credentials 'username', 'password' +end diff --git a/config/routes.rb b/config/routes.rb index 5b27eba4..83e65707 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Rails.application.routes.draw do + mount Rswag::Ui::Engine => '/api-docs' + mount Rswag::Api::Engine => '/api-docs' get "order_comments/new" get "comments/new" diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb new file mode 100644 index 00000000..8e1e23fb --- /dev/null +++ b/spec/requests/api/users_spec.rb @@ -0,0 +1,22 @@ +require 'swagger_helper' + +describe 'Users API', type: :request do + path '/user' do + get 'info about the currently logged-in user' do + tags '1. User' + produces 'application/json' + + response '200', 'success' do + run_test! do |response| + let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" } + data = JSON.parse(response.body) + # expect(data[]) + end + end + + response '401', 'not logged-in' do + run_test! + end + end + end +end diff --git a/spec/swagger_helper.rb b/spec/swagger_helper.rb new file mode 100644 index 00000000..0cfa8370 --- /dev/null +++ b/spec/swagger_helper.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.configure do |config| + # Specify a root folder where Swagger JSON files are generated + # NOTE: If you're using the rswag-api to serve API descriptions, you'll need + # to ensure that it's configured to serve Swagger from the same folder + config.swagger_root = Rails.root.join('swagger').to_s + + # Define one or more Swagger documents and provide global metadata for each one + # When you run the 'rswag:specs: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.yaml' => { + openapi: '3.0.1', + info: { + title: 'API V1', + version: 'v1' + }, + paths: {}, + components: { + securitySchemes: { + oauth2: { + type: :oauth2, + in: :header, + name: 'Authorization', + flows: { + implicit: { + authorizationUrl: 'http://localhost:3000/f/oauth/authorize', + scopes: { + 'config:user': 'reading Foodsoft configuration for regular users', + 'config:read': 'reading Foodsoft configuration values', + 'config:write': 'reading and updating Foodsoft configuration values', + 'finance:user': 'accessing your own financial transactions', + 'finance:read': 'reading all financial transactions', + 'finance:write': 'reading and creating financial transactions', + 'user:read': 'reading your own user profile', + 'user:write': 'reading and updating your own user profile', + offline_access: 'retain access after user has logged out', + } + } + } + } + } + }, + servers: [ + { + url: 'http://{defaultHost}/f/api/v1', + variables: { + defaultHost: { + default: 'localhost:3000' + } + } + } + ], + security: [ + oauth2: [ + 'user:read', + ], + ], + } + } + + # Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'. + # The swagger_docs configuration option has the filename including format in + # the key, this may want to be changed to avoid putting yaml in json files. + # Defaults to json. Accepts ':json' and ':yaml'. + config.swagger_format = :yaml +end diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml new file mode 100644 index 00000000..549db1d2 --- /dev/null +++ b/swagger/v1/swagger.yaml @@ -0,0 +1,43 @@ +--- +openapi: 3.0.1 +info: + title: API V1 + version: v1 +paths: + "/user": + get: + summary: info about the currently logged-in user + tags: + - 1. User + responses: + '200': + description: success + '401': + description: not logged-in +components: + securitySchemes: + oauth2: + type: oauth2 + in: header + name: Authorization + flows: + implicit: + authorizationUrl: http://localhost:3000/f/oauth/authorize + scopes: + config:user: reading Foodsoft configuration for regular users + config:read: reading Foodsoft configuration values + config:write: reading and updating Foodsoft configuration values + finance:user: accessing your own financial transactions + finance:read: reading all financial transactions + finance:write: reading and creating financial transactions + user:read: reading your own user profile + user:write: reading and updating your own user profile + offline_access: retain access after user has logged out +servers: +- url: http://{defaultHost}/f/api/v1 + variables: + defaultHost: + default: localhost:3000 +security: +- oauth2: + - user:read