oas_core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +70 -0
  4. data/Rakefile +12 -0
  5. data/lib/oas_core/builders/content_builder.rb +44 -0
  6. data/lib/oas_core/builders/operation_builder.rb +94 -0
  7. data/lib/oas_core/builders/parameter_builder.rb +31 -0
  8. data/lib/oas_core/builders/parameters_builder.rb +46 -0
  9. data/lib/oas_core/builders/path_item_builder.rb +26 -0
  10. data/lib/oas_core/builders/request_body_builder.rb +45 -0
  11. data/lib/oas_core/builders/response_builder.rb +42 -0
  12. data/lib/oas_core/builders/responses_builder.rb +92 -0
  13. data/lib/oas_core/builders/specification_builder.rb +24 -0
  14. data/lib/oas_core/configuration.rb +143 -0
  15. data/lib/oas_core/json_schema_generator.rb +134 -0
  16. data/lib/oas_core/oas_route.rb +24 -0
  17. data/lib/oas_core/spec/components.rb +99 -0
  18. data/lib/oas_core/spec/contact.rb +20 -0
  19. data/lib/oas_core/spec/hashable.rb +41 -0
  20. data/lib/oas_core/spec/info.rb +68 -0
  21. data/lib/oas_core/spec/license.rb +20 -0
  22. data/lib/oas_core/spec/media_type.rb +26 -0
  23. data/lib/oas_core/spec/operation.rb +28 -0
  24. data/lib/oas_core/spec/parameter.rb +39 -0
  25. data/lib/oas_core/spec/path_item.rb +27 -0
  26. data/lib/oas_core/spec/paths.rb +28 -0
  27. data/lib/oas_core/spec/reference.rb +18 -0
  28. data/lib/oas_core/spec/request_body.rb +23 -0
  29. data/lib/oas_core/spec/response.rb +22 -0
  30. data/lib/oas_core/spec/responses.rb +27 -0
  31. data/lib/oas_core/spec/server.rb +19 -0
  32. data/lib/oas_core/spec/specable.rb +56 -0
  33. data/lib/oas_core/spec/specification.rb +34 -0
  34. data/lib/oas_core/spec/tag.rb +20 -0
  35. data/lib/oas_core/string.rb +13 -0
  36. data/lib/oas_core/utils.rb +94 -0
  37. data/lib/oas_core/version.rb +5 -0
  38. data/lib/oas_core/yard/example_tag.rb +14 -0
  39. data/lib/oas_core/yard/oas_core_factory.rb +166 -0
  40. data/lib/oas_core/yard/parameter_tag.rb +16 -0
  41. data/lib/oas_core/yard/request_body_example_tag.rb +13 -0
  42. data/lib/oas_core/yard/request_body_tag.rb +17 -0
  43. data/lib/oas_core/yard/response_example_tag.rb +14 -0
  44. data/lib/oas_core/yard/response_tag.rb +15 -0
  45. data/lib/oas_core.rb +88 -0
  46. metadata +120 -0
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ ![Gem Version](https://img.shields.io/gem/v/oas_core?color=E9573F)
2
+ ![GitHub License](https://img.shields.io/github/license/a-chacon/oas_core?color=blue)
3
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/a-chacon/oas_core/.github%2Fworkflows%2Frubyonrails.yml)
4
+ ![Gem Total Downloads](https://img.shields.io/gem/dt/oas_core)
5
+ ![Static Badge](https://img.shields.io/badge/Rails-%3E%3D7.0.0-%23E9573F)
6
+ ![Static Badge](https://img.shields.io/badge/Ruby-%3E%3D3.1.0-%23E9573F)
7
+
8
+ # πŸ“ƒOpen API Specification For Rails
9
+
10
+ OasCore is a Rails engine for generating **automatic interactive documentation for your Rails APIs**. It generates an **OAS 3.1** document and displays it using **[RapiDoc](https://rapidocweb.com)**.
11
+
12
+ ### πŸš€ Demo App
13
+
14
+ Explore the interactive documentation live:
15
+
16
+ πŸ”— **[Open Demo App](https://paso.fly.dev/api/docs)**
17
+ πŸ‘€ **Username**: `oasrails`
18
+ πŸ”‘ **Password**: `oasrails`
19
+
20
+ 🎬 A Demo Installation/Usage Video:
21
+ <https://vimeo.com/1013687332>
22
+ 🎬
23
+
24
+ ![Screenshot](https://a-chacon.com/assets/images/oas_core_ui.png)
25
+
26
+ ## Related Projects
27
+
28
+ - **[ApiPie](https://github.com/Apipie/apipie-rails)**: Doesn't support OAS 3.1, requires learning a DSL, lacks a nice UI
29
+ - **[swagger_yard-rails](https://github.com/livingsocial/swagger_yard-rails)**: Seems abandoned, but serves as inspiration
30
+ - **[Rswag](https://github.com/rswag/rswag)**: Not automatic, depends on RSpec; Many developers now use Minitest as it's the default test framework
31
+ - **[grape-swagger](https://github.com/ruby-grape/grape-swagger)**: Requires Grape
32
+ - **[rspec_api_documentation](https://github.com/zipmark/rspec_api_documentation)**: Requires RSpec and a command to generate the docs
33
+
34
+ ## What Sets OasCore Apart?
35
+
36
+ - **Dynamic**: No command required to generate docs
37
+ - **Simple**: Complement default documentation with a few comments; no need to learn a complex DSL
38
+ - **Pure Ruby on Rails APIs**: No additional frameworks needed (e.g., Grape, RSpec)
39
+
40
+ ## πŸ“½οΈ Motivation
41
+
42
+ After experiencing the interactive documentation in Python's fast-api framework, I sought similar functionality in Ruby on Rails. Unable to find a suitable solution, I [asked on Stack Overflow](https://stackoverflow.com/questions/71947018/is-there-a-way-to-generate-an-interactive-documentation-for-rails-apis) years ago. Now, with some free time while freelancing as an API developer, I decided to build my own tool.
43
+
44
+ **Note: This is not yet a production-ready solution. The code may be rough and behave unexpectedly, but I am actively working on improving it. If you like the idea, please consider contributing to its development.**
45
+
46
+ The goal is to minimize the effort required to create comprehensive documentation. By following REST principles in Rails, we believe this is achievable. You can enhance the documentation using [Yard](https://yardoc.org/) tags.
47
+
48
+ ## Documentation
49
+
50
+ For see how to install, configure and use OasCore please refere to the [OasCoreBook](http://a-chacon.com/oas_core)
51
+
52
+ ## Contributing
53
+
54
+ Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star⭐! Thanks again!
55
+
56
+ If you plan a big feature, first open an issue to discuss it before any development.
57
+
58
+ 1. Fork the Project
59
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
60
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
61
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
62
+ 5. Open a Pull Request
63
+
64
+ ## License
65
+
66
+ The gem is available as open source under the terms of the [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.en.html#license-text).
67
+
68
+ ## Star History
69
+
70
+ [![Star History Chart](https://api.star-history.com/svg?repos=a-chacon/oas_core&type=Date)](https://www.star-history.com/#a-chacon/oas_core&Date)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'minitest/test_task'
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class ContentBuilder
6
+ def initialize(specification, context)
7
+ @context = context || :incoming
8
+ @specification = specification
9
+ @media_type = Spec::MediaType.new(specification)
10
+ end
11
+
12
+ def with_schema(schema)
13
+ @media_type.schema = @specification.components.add_schema(schema)
14
+
15
+ self
16
+ end
17
+
18
+ def with_examples(examples)
19
+ @media_type.examples = @specification.components.add_example(examples)
20
+
21
+ self
22
+ end
23
+
24
+ def with_examples_from_tags(tags)
25
+ @media_type.examples = @media_type.examples.merge(tags.each_with_object({}).with_index(1) do |(example, result), _index|
26
+ key = example.text.downcase.gsub(' ', '_')
27
+ value = {
28
+ 'summary' => example.text,
29
+ 'value' => example.content
30
+ }
31
+ result[key] = @specification.components.add_example(value)
32
+ end)
33
+
34
+ self
35
+ end
36
+
37
+ def build
38
+ {
39
+ 'application/json': @media_type
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class OperationBuilder
6
+ def initialize(specification)
7
+ @specification = specification
8
+ @operation = Spec::Operation.new(specification)
9
+ end
10
+
11
+ def from_oas_route(oas_route)
12
+ @operation.summary = extract_summary(oas_route:)
13
+ @operation.operation_id = extract_operation_id(oas_route:)
14
+ @operation.description = oas_route.docstring
15
+ @operation.tags = extract_tags(oas_route:)
16
+ @operation.security = extract_security(oas_route:)
17
+ @operation.parameters = ParametersBuilder.new(@specification).from_oas_route(oas_route).build
18
+ @operation.request_body = RequestBodyBuilder.new(@specification).from_oas_route(oas_route).reference
19
+ @operation.responses = ResponsesBuilder.new(@specification)
20
+ .from_oas_route(oas_route)
21
+ .add_default_responses(oas_route, !@operation.security.empty?).build
22
+
23
+ self
24
+ end
25
+
26
+ def build
27
+ @operation
28
+ end
29
+
30
+ private
31
+
32
+ def extract_summary(oas_route:)
33
+ oas_route.tags(:summary).first.try(:text) || generate_crud_name(oas_route.method_name,
34
+ oas_route.controller.downcase) || "#{oas_route.verb} #{oas_route.path}"
35
+ end
36
+
37
+ def extract_operation_id(oas_route:)
38
+ "#{oas_route.method_name}#{oas_route.path.gsub('/', '_').gsub(/[{}]/, '')}"
39
+ end
40
+
41
+ def extract_tags(oas_route:)
42
+ tags = oas_route.tags(:tags).first
43
+ if tags.nil?
44
+ default_tags(oas_route:)
45
+ else
46
+ tags.text.split(',').map(&:strip).map(&:titleize)
47
+ end
48
+ end
49
+
50
+ def default_tags(oas_route:)
51
+ tags = []
52
+ if OasCore.config.default_tags_from == :namespace
53
+ tag = oas_route.path.split('/').reject(&:empty?).first.try(:titleize)
54
+ tags << tag unless tag.nil?
55
+ else
56
+ tags << oas_route.controller.gsub('/', ' ').titleize
57
+ end
58
+
59
+ tags
60
+ end
61
+
62
+ def extract_security(oas_route:)
63
+ return [] if oas_route.tags(:no_auth).any?
64
+
65
+ if (methods = oas_route.tags(:auth).first)
66
+ OasCore.config.security_schemas.keys.map { |key| { key => [] } }.select do |schema|
67
+ methods.types.include?(schema.keys.first.to_s)
68
+ end
69
+ elsif OasCore.config.authenticate_all_routes_by_default
70
+ OasCore.config.security_schemas.keys.map { |key| { key => [] } }
71
+ else
72
+ []
73
+ end
74
+ end
75
+
76
+ def generate_crud_name(method, controller)
77
+ controller_name = controller.to_s.underscore.humanize.downcase.pluralize
78
+
79
+ case method.to_sym
80
+ when :index
81
+ "List #{controller_name}"
82
+ when :show
83
+ "View #{controller_name.singularize}"
84
+ when :create
85
+ "Create new #{controller_name.singularize}"
86
+ when :update
87
+ "Update #{controller_name.singularize}"
88
+ when :destroy
89
+ "Delete #{controller_name.singularize}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class ParameterBuilder
6
+ def initialize(specification)
7
+ @specification = specification
8
+ @parameter = Spec::Parameter.new(specification)
9
+ end
10
+
11
+ def from_path(path, param)
12
+ @parameter.name = param
13
+ @parameter.in = 'path'
14
+ @parameter.description = "#{param.split('_')[-1].titleize} of existing #{extract_word_before(path,
15
+ param).singularize}."
16
+
17
+ self
18
+ end
19
+
20
+ def extract_word_before(string, param)
21
+ regex = %r{/([\w-]+)/\{#{param}\}}
22
+ match = string.match(regex)
23
+ match ? match[1] : ''
24
+ end
25
+
26
+ def build
27
+ @parameter
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class ParametersBuilder
6
+ def initialize(specification)
7
+ @specification = specification
8
+ @parameters = []
9
+ end
10
+
11
+ def from_oas_route(oas_route)
12
+ parameters_from_tags(tags: oas_route.tags(:parameter))
13
+ oas_route.path_params.try(:map) do |p|
14
+ unless @parameters.any? do |param|
15
+ param.name.to_s == p.to_s
16
+ end
17
+ @parameters << ParameterBuilder.new(@specification).from_path(oas_route.path,
18
+ p).build
19
+ end
20
+ end
21
+
22
+ self
23
+ end
24
+
25
+ def parameters_from_tags(tags:)
26
+ tags.each do |t|
27
+ parameter = Spec::Parameter.new(@specification)
28
+ parameter.name = t.name
29
+ parameter.in = t.location
30
+ parameter.required = t.required
31
+ parameter.schema = t.schema
32
+ parameter.description = t.text
33
+ @parameters << parameter
34
+ end
35
+
36
+ self
37
+ end
38
+
39
+ def build
40
+ @parameters.map do |p|
41
+ @specification.components.add_parameter(p)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class PathItemBuilder
6
+ def initialize(specification)
7
+ @specification = specification
8
+ @path_item = Spec::PathItem.new(specification)
9
+ end
10
+
11
+ def with_oas_routes(oas_routes)
12
+ oas_routes.each do |oas_route|
13
+ oas_route.verb.downcase.split('|').each do |v|
14
+ @path_item.add_operation(v, OperationBuilder.new(@specification).from_oas_route(oas_route).build)
15
+ end
16
+ end
17
+
18
+ self
19
+ end
20
+
21
+ def build
22
+ @path_item
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class RequestBodyBuilder
6
+ def initialize(specification)
7
+ @specification = specification
8
+ @request_body = Spec::RequestBody.new(specification)
9
+ end
10
+
11
+ def from_oas_route(oas_route)
12
+ tag_request_body = oas_route.tags(:request_body).first
13
+ # TODO: This is for frameowkr specific.
14
+ # if tag_request_body.nil? && OasCore.config.autodiscover_request_body
15
+ # detect_request_body(oas_route) if %w[create update].include? oas_route.method_name
16
+ return self if tag_request_body.nil?
17
+
18
+ from_tags(tag: tag_request_body, examples_tags: oas_route.tags(:request_body_example))
19
+
20
+ self
21
+ end
22
+
23
+ def from_tags(tag:, examples_tags: [])
24
+ @request_body.description = tag.text
25
+ @request_body.content = ContentBuilder.new(@specification,
26
+ :incoming).with_schema(tag.schema).with_examples_from_tags(examples_tags).build
27
+ @request_body.required = tag.required
28
+
29
+ self
30
+ end
31
+
32
+ def build
33
+ return {} if @request_body.content == {}
34
+
35
+ @request_body
36
+ end
37
+
38
+ def reference
39
+ return {} if @request_body.content == {}
40
+
41
+ @specification.components.add_request_body(@request_body)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class ResponseBuilder
6
+ def initialize(specification)
7
+ @specification = specification
8
+ @response = Spec::Response.new(specification)
9
+ end
10
+
11
+ def with_description(description)
12
+ @response.description = description
13
+
14
+ self
15
+ end
16
+
17
+ def with_content(content)
18
+ @response.content = content
19
+
20
+ self
21
+ end
22
+
23
+ def with_code(code)
24
+ @response.code = code
25
+
26
+ self
27
+ end
28
+
29
+ def from_tag(tag)
30
+ @response.code = tag.name.to_i
31
+ @response.description = tag.text
32
+ @response.content = ContentBuilder.new(@specification, :outgoing).with_schema(tag.schema).build
33
+
34
+ self
35
+ end
36
+
37
+ def build
38
+ @response
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class ResponsesBuilder
6
+ def initialize(specification, content_builder: ContentBuilder, response_builder: ResponseBuilder,
7
+ json_schema_generator: JsonSchemaGenerator, utils: Utils)
8
+ @specification = specification
9
+ @content_builder = content_builder
10
+ @response_builder = response_builder
11
+ @json_schema_generator = json_schema_generator
12
+ @utils = utils
13
+ @responses = Spec::Responses.new(specification)
14
+ end
15
+
16
+ def from_oas_route(oas_route)
17
+ oas_route.tags(:response).each do |tag|
18
+ content = @content_builder.new(@specification,
19
+ :outgoing).with_schema(tag.schema).with_examples_from_tags(oas_route.tags(:response_example).filter do |re|
20
+ re.code == tag.name
21
+ end).build
22
+ response = @response_builder.new(@specification).with_code(tag.name.to_i).with_description(tag.text).with_content(content).build
23
+
24
+ @responses.add_response(response)
25
+ end
26
+
27
+ self
28
+ end
29
+
30
+ # def add_autodiscovered_responses(oas_route)
31
+ # return self if !OasCore.config.autodiscover_responses || oas_route.tags(:response).any?
32
+ #
33
+ # new_responses = Extractors::RenderResponseExtractor.extract_responses_from_source(@specification, source: oas_route.source_string)
34
+ #
35
+ # new_responses.each do |new_response|
36
+ # @responses.add_response(new_response) if @responses.responses[new_response.code].blank?
37
+ # end
38
+ #
39
+ # self
40
+ # end
41
+
42
+ def add_default_responses(oas_route, security)
43
+ return self unless OasCore.config.set_default_responses
44
+
45
+ common_errors = determine_common_errors(oas_route, security)
46
+ add_responses_for_errors(common_errors)
47
+
48
+ self
49
+ end
50
+
51
+ def build
52
+ @responses
53
+ end
54
+
55
+ private
56
+
57
+ def determine_common_errors(oas_route, security)
58
+ common_errors = []
59
+ common_errors.push(:unauthorized, :forbidden, :internal_server_error) if security
60
+
61
+ case oas_route.method_name
62
+ when 'show', 'destroy'
63
+ common_errors.push(:not_found)
64
+ when 'create'
65
+ common_errors.push(:unprocessable_entity)
66
+ when 'update'
67
+ common_errors.push(:not_found, :unprocessable_entity)
68
+ end
69
+
70
+ OasCore.config.possible_default_responses & common_errors
71
+ end
72
+
73
+ def add_responses_for_errors(errors)
74
+ errors.each do |error|
75
+ response_body = resolve_response_body(error)
76
+ content = @content_builder.new(@specification,
77
+ :outgoing).with_schema(@json_schema_generator.process_string(response_body)[:json_schema]).build
78
+ code = @utils.status_to_integer(error)
79
+ response = @response_builder.new(@specification).with_code(code).with_description(@utils.get_definition(code)).with_content(content).build
80
+
81
+ @responses.add_response(response) if @responses.responses[response.code].nil?
82
+ end
83
+ end
84
+
85
+ def resolve_response_body(error)
86
+ OasCore.config.public_send("response_body_of_#{error}")
87
+ rescue StandardError
88
+ OasCore.config.response_body_of_default
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module Builders
5
+ class SpecificationBuilder
6
+ def initialize
7
+ @specification = Spec::Specification.new
8
+ end
9
+
10
+ def with_oas_routes(oas_routes)
11
+ grouped_routes = oas_routes.group_by(&:path)
12
+ grouped_routes.each do |path, routes|
13
+ @specification.paths.add_path(path, routes)
14
+ end
15
+
16
+ self
17
+ end
18
+
19
+ def build
20
+ @specification
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ class Configuration
5
+ attr_accessor :info,
6
+ :layout,
7
+ :default_tags_from,
8
+ :autodiscover_request_body,
9
+ :autodiscover_responses,
10
+ :api_path,
11
+ :ignored_actions,
12
+ :security_schemas,
13
+ :authenticate_all_routes_by_default,
14
+ :set_default_responses,
15
+ :possible_default_responses,
16
+ :http_verbs,
17
+ :use_model_names,
18
+ :rapidoc_theme
19
+
20
+ attr_reader :servers, :tags, :security_schema, :include_mode, :response_body_of_default
21
+
22
+ def initialize
23
+ @info = Spec::Info.new
24
+ @layout = false
25
+ @servers = default_servers
26
+ @tags = []
27
+ @swagger_version = '3.1.0'
28
+ @default_tags_from = :namespace
29
+ @autodiscover_request_body = true
30
+ @autodiscover_responses = true
31
+ @api_path = '/'
32
+ @ignored_actions = []
33
+ @authenticate_all_routes_by_default = true
34
+ @security_schema = nil
35
+ @security_schemas = {}
36
+ @set_default_responses = true
37
+ @possible_default_responses = %i[not_found unauthorized forbidden internal_server_error
38
+ unprocessable_entity]
39
+ @http_verbs = %i[get post put patch delete]
40
+ @response_body_of_default = 'Hash{ status: !Integer, error: String }'
41
+ @use_model_names = false
42
+ @rapidoc_theme = :rails
43
+ @include_mode = :all
44
+
45
+ @possible_default_responses.each do |response|
46
+ method_name = "response_body_of_#{response}="
47
+ variable_name = "@response_body_of_#{response}"
48
+
49
+ define_singleton_method(method_name) do |value|
50
+ raise ArgumentError, "#{method_name} must be a String With a valid object" unless value.is_a?(String)
51
+
52
+ OasCore::JsonSchemaGenerator.parse_type(value)
53
+ instance_variable_set(variable_name, value)
54
+ end
55
+
56
+ define_singleton_method("response_body_of_#{response}") do
57
+ instance_variable_get(variable_name) || @response_body_of_default
58
+ end
59
+ end
60
+ end
61
+
62
+ def security_schema=(value)
63
+ return unless (security_schema = DEFAULT_SECURITY_SCHEMES[value])
64
+
65
+ @security_schemas = { value => security_schema }
66
+ end
67
+
68
+ def default_servers
69
+ [Spec::Server.new(url: 'http://localhost:3000', description: 'Rails Default Development Server')]
70
+ end
71
+
72
+ def servers=(value)
73
+ @servers = value.map { |s| Spec::Server.new(url: s[:url], description: s[:description]) }
74
+ end
75
+
76
+ def tags=(value)
77
+ @tags = value.map { |t| Spec::Tag.new(name: t[:name], description: t[:description]) }
78
+ end
79
+
80
+ def excluded_columns_incoming
81
+ %i[id created_at updated_at deleted_at]
82
+ end
83
+
84
+ def excluded_columns_outgoing
85
+ []
86
+ end
87
+
88
+ def include_mode=(value)
89
+ valid_modes = %i[all with_tags explicit]
90
+ raise ArgumentError, "include_mode must be one of #{valid_modes}" unless valid_modes.include?(value)
91
+
92
+ @include_mode = value
93
+ end
94
+
95
+ def response_body_of_default=(value)
96
+ raise ArgumentError, 'response_body_of_default must be a String With a valid object' unless value.is_a?(String)
97
+
98
+ OasCore::JsonSchemaGenerator.parse_type(value)
99
+ @response_body_of_default = value
100
+ end
101
+ end
102
+
103
+ DEFAULT_SECURITY_SCHEMES = {
104
+ api_key_cookie: {
105
+ type: 'apiKey',
106
+ in: 'cookie',
107
+ name: 'api_key',
108
+ description: 'An API key that will be supplied in a named cookie.'
109
+ },
110
+ api_key_header: {
111
+ type: 'apiKey',
112
+ in: 'header',
113
+ name: 'X-API-Key',
114
+ description: 'An API key that will be supplied in a named header.'
115
+ },
116
+ api_key_query: {
117
+ type: 'apiKey',
118
+ in: 'query',
119
+ name: 'apiKey',
120
+ description: 'An API key that will be supplied in a named query parameter.'
121
+ },
122
+ basic: {
123
+ type: 'http',
124
+ scheme: 'basic',
125
+ description: "Basic auth that takes a base64'd combination of `user:password`."
126
+ },
127
+ bearer: {
128
+ type: 'http',
129
+ scheme: 'bearer',
130
+ description: 'A bearer token that will be supplied within an `Authorization` header as `bearer <token>`.'
131
+ },
132
+ bearer_jwt: {
133
+ type: 'http',
134
+ scheme: 'bearer',
135
+ bearerFormat: 'JWT',
136
+ description: 'A bearer token that will be supplied within an `Authorization` header as `bearer <token>`. In this case, the format of the token is specified as JWT.'
137
+ },
138
+ mutual_tls: {
139
+ type: 'mutualTLS',
140
+ description: 'Requires a specific mutual TLS certificate to use when making an HTTP request.'
141
+ }
142
+ }.freeze
143
+ end