diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66429c9b3..4996a2add 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
#### Features
-* Your contribution here.
+* [#888](https://github.com/ruby-grape/grape-swagger/pull/888): Add default_route_visibility to easily hide all endpoints - [@dmoss18](https://github.com/dmoss18)
#### Fixes
diff --git a/README.md b/README.md
index 4451eddd7..a507a38cb 100644
--- a/README.md
+++ b/README.md
@@ -206,6 +206,7 @@ end
* [array_use_braces](#array_use_braces)
* [api_documentation](#api_documentation)
* [specific_api_documentation](#specific_api_documentation)
+* [default_route_visibility](#default_route_visibility)
* [consumes](#consumes)
* [produces](#produces)
@@ -434,6 +435,23 @@ add_swagger_documentation \
specific_api_documentation: { desc: 'Reticulated splines API swagger-compatible endpoint documentation.' }
```
+#### default_route_visibility
+By default, grape-swagger will include all routes in the genrated documentation. You can configure this via `default_route_visibility`:
+```rb
+add_swagger_documentation \
+ default_route_visibility: :hidden
+```
+Grape-swagger will now *exclude* all routes in the generated documentation. You can then explicitly mark specific routes public like this:
+```rb
+desc 'Get all accounts', public: true
+get 'accounts' do
+ ['account1', 'account2']
+end
+```
+
+**Please note**: Marking a route `public: false` or `hidden: false` will simply fall back to the overall `default_route_visibility`. You should consider `public` and `hidden` to be flags that only take effect when their value is truthy.
+
+
#### consumes
Customize the Swagger API default global `consumes` field value.
@@ -452,10 +470,12 @@ add_swagger_documentation \
produces: ['text/plain']
```
+
## Routes Configuration
* [Swagger Header Parameters](#headers)
* [Hiding an Endpoint](#hiding)
+* [Hiding all Endpoints](#hiding-all-endpoints)
* [Overriding Auto-Generated Nicknames](#overriding-auto-generated-nicknames)
* [Specify endpoint details](#details)
* [Overriding the route summary](#summary)
@@ -530,6 +550,34 @@ state:
desc 'Conditionally hide this endpoint', hidden: lambda { ENV['EXPERIMENTAL'] != 'true' }
```
+#### Hiding all endpoints
+You can hide all endpoints by default via `default_endpoint_visibility: :hidden`. You'll need to explicitly add `public: true` to each endpoint. This is functionally the inverse of the above [Hiding an Endpoint](#hiding) section.
+
+You can show an endpoint by adding ```public: true``` in the description of the endpoint:
+```ruby
+desc 'Show this endpoint', public: true
+```
+
+Or by adding ```public: true``` on the verb method of the endpoint, such as `get`, `post` and `put`:
+
+```ruby
+get '/kittens', public: true do
+```
+
+Or by using a route setting:
+
+```ruby
+route_setting :swagger, { public: true }
+get '/kittens' do
+```
+
+Endpoints can be conditionally shown by providing a callable object such as a lambda which evaluates to the desired
+state:
+
+```ruby
+desc 'Conditionally hide this endpoint', public: lambda { ENV['EXPERIMENTAL'] != 'true' }
+```
+
#### Overriding Auto-Generated Nicknames
diff --git a/lib/grape-swagger/doc_methods.rb b/lib/grape-swagger/doc_methods.rb
index efb850e34..820476dda 100644
--- a/lib/grape-swagger/doc_methods.rb
+++ b/lib/grape-swagger/doc_methods.rb
@@ -38,7 +38,8 @@ module DocMethods
specific_api_documentation: { desc: 'Swagger compatible API description for specific API' },
endpoint_auth_wrapper: nil,
swagger_endpoint_guard: nil,
- token_owner: nil
+ token_owner: nil,
+ default_route_visibility: :public
}.freeze
FORMATTER_METHOD = %i[format default_format default_error_formatter].freeze
diff --git a/lib/grape-swagger/endpoint.rb b/lib/grape-swagger/endpoint.rb
index 27e123dd5..649240164 100644
--- a/lib/grape-swagger/endpoint.rb
+++ b/lib/grape-swagger/endpoint.rb
@@ -3,6 +3,7 @@
require 'active_support'
require 'active_support/core_ext/string/inflections'
require 'grape-swagger/endpoint/params_parser'
+require 'grape-swagger/endpoint/visibility'
module Grape
class Endpoint # rubocop:disable Metrics/ClassLength
@@ -96,7 +97,7 @@ def add_definitions_from(models)
# path object
def path_item(routes, options)
routes.each do |route|
- next if hidden?(route, options)
+ next if GrapeSwagger::Endpoint::Visibility.hidden_route?(route, options)
@item, path = GrapeSwagger::DocMethods::PathString.build(route, options)
@entity = route.entity || route.options[:success]
@@ -177,7 +178,7 @@ def consumes_object(route, format)
def params_object(route, options, path)
parameters = build_request_params(route, options).each_with_object([]) do |(param, value), memo|
- next if hidden_parameter?(value)
+ next if GrapeSwagger::Endpoint::Visibility.hidden_parameter?(value)
value = { required: false }.merge(value) if value.is_a?(Hash)
_, value = default_type([[param, value]]).first if value == ''
@@ -435,24 +436,6 @@ def model_name(name)
GrapeSwagger::DocMethods::DataType.parse_entity_name(name)
end
- def hidden?(route, options)
- route_hidden = route.settings.try(:[], :swagger).try(:[], :hidden)
- route_hidden = route.options[:hidden] if route.options.key?(:hidden)
- return route_hidden unless route_hidden.is_a?(Proc)
-
- options[:token_owner] ? route_hidden.call(send(options[:token_owner].to_sym)) : route_hidden.call
- end
-
- def hidden_parameter?(value)
- return false if value[:required]
-
- if value.dig(:documentation, :hidden).is_a?(Proc)
- value.dig(:documentation, :hidden).call
- else
- value.dig(:documentation, :hidden)
- end
- end
-
def success_code_from_entity(route, entity)
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
if entity.is_a?(Hash)
diff --git a/lib/grape-swagger/endpoint/visibility.rb b/lib/grape-swagger/endpoint/visibility.rb
new file mode 100644
index 000000000..a3f8eeace
--- /dev/null
+++ b/lib/grape-swagger/endpoint/visibility.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module GrapeSwagger
+ module Endpoint
+ class Visibility
+ class << self
+ def hidden_route?(route, options)
+ return !public_route?(route, options) if options[:default_route_visibility] == :hidden
+
+ scan_route_for_value(:hidden, route, options)
+ end
+
+ def public_route?(route, options)
+ scan_route_for_value(:public, route, options)
+ end
+
+ def hidden_parameter?(value)
+ return false if value[:required]
+
+ if value.dig(:documentation, :hidden).is_a?(Proc)
+ value.dig(:documentation, :hidden).call
+ else
+ value.dig(:documentation, :hidden)
+ end
+ end
+
+ private
+
+ def scan_route_for_value(key, route, options)
+ key = key.to_sym
+ route_value = route.settings.try(:[], :swagger).try(:[], key)
+ route_value = route.options[key] if route.options.key?(key)
+ return route_value unless route_value.is_a?(Proc)
+
+ options[:token_owner] ? route_value.call(send(options[:token_owner].to_sym)) : route_value.call
+ end
+ end
+ end
+ end
+end
diff --git a/spec/issues/888_default_route_visbility_spec.rb b/spec/issues/888_default_route_visbility_spec.rb
new file mode 100644
index 000000000..cace69546
--- /dev/null
+++ b/spec/issues/888_default_route_visbility_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'default endpoint visibility' do
+ let(:documentation_options) do
+ { default_route_visibility: default_visibility }
+ end
+ let(:app) do
+ swagger_options = documentation_options
+ options = route_options
+
+ Class.new(Grape::API) do
+ desc 'Get all accounts', options
+ resource :accounts do
+ get do
+ [{ message: 'hello world' }]
+ end
+ end
+
+ add_swagger_documentation(swagger_options)
+ end
+ end
+
+ shared_examples 'public endpoint' do
+ it 'exposes endpoint' do
+ get_route = subject.dig('paths', '/accounts', 'get')
+ expect(get_route).to be_present
+ expect(get_route['description']).to eq 'Get all accounts'
+ end
+ end
+
+ shared_examples 'hidden endpoint' do
+ it 'hides endpoint' do
+ expect(subject.dig('paths', '/accounts')).to be_nil
+ end
+ end
+
+ subject do
+ get '/swagger_doc'
+ JSON.parse(last_response.body)
+ end
+
+ context 'with :public default visibility' do
+ let(:default_visibility) { :public }
+
+ context 'with endpoint marked hidden: true' do
+ let(:route_options) do
+ { hidden: true }
+ end
+
+ it_behaves_like 'hidden endpoint'
+ end
+
+ context 'with endpoint marked public: true' do
+ let(:route_options) do
+ { public: true }
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+
+ context 'with blank endpoint options' do
+ let(:route_options) do
+ {}
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+
+ context 'with endpoint marked hidden: false' do
+ let(:route_options) do
+ { hidden: false }
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+
+ context 'with endpoint marked public: false' do
+ let(:route_options) do
+ { public: false }
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+ end
+
+ context 'with :hidden default visibility' do
+ let(:default_visibility) { :hidden }
+
+ context 'with endpoint marked public: true' do
+ let(:route_options) do
+ { public: true }
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+
+ context 'with endpoint marked hidden: true' do
+ let(:route_options) do
+ { hidden: true }
+ end
+
+ it_behaves_like 'hidden endpoint'
+ end
+
+ context 'with blank endpoint options' do
+ let(:route_options) do
+ {}
+ end
+
+ it_behaves_like 'hidden endpoint'
+ end
+
+ context 'with endpoint marked public: false' do
+ let(:route_options) do
+ { public: false }
+ end
+
+ it_behaves_like 'hidden endpoint'
+ end
+
+ context 'with endpoint marked hidden: false' do
+ let(:route_options) do
+ { hidden: false }
+ end
+
+ it_behaves_like 'hidden endpoint'
+ end
+ end
+
+ context 'with no visibility specified' do
+ let(:documentation_options) do
+ {}
+ end
+
+ context 'with endpoint marked public: true' do
+ let(:route_options) do
+ { public: true }
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+
+ context 'with endpoint marked hidden: true' do
+ let(:route_options) do
+ { hidden: true }
+ end
+
+ it_behaves_like 'hidden endpoint'
+ end
+
+ context 'with blank endpoint options' do
+ let(:route_options) do
+ {}
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+
+ context 'with endpoint marked public: false' do
+ let(:route_options) do
+ { public: false }
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+
+ context 'with endpoint marked hidden: false' do
+ let(:route_options) do
+ { hidden: false }
+ end
+
+ it_behaves_like 'public endpoint'
+ end
+ end
+end
diff --git a/spec/lib/oapi_tasks_spec.rb b/spec/lib/oapi_tasks_spec.rb
index 38bdca700..ab6626563 100644
--- a/spec/lib/oapi_tasks_spec.rb
+++ b/spec/lib/oapi_tasks_spec.rb
@@ -34,7 +34,7 @@ class Base < Grape::API
end
it 'accepts class name as a string' do
- expect(described_class.new('::Api::Base').send(:api_class)).to eq(Api::Base)
+ expect(described_class.new('Api::Base').send(:api_class)).to eq(Api::Base)
end
end