oas_rails 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,7 +1,13 @@
1
+ ![Gem Version](https://img.shields.io/gem/v/oas_rails)
2
+ ![GitHub License](https://img.shields.io/github/license/a-chacon/oas_rails)
3
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/a-chacon/oas_rails/.github%2Fworkflows%2Frubyonrails.yml)
4
+
1
5
  # Open API Specification For Rails
2
6
 
3
7
  OasRails 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)**.
4
8
 
9
+ ![Screenshot](https://raw.githubusercontent.com/a-chacon/oas_rails/0cfc9abb5be85e6bb3fc4669e29372be8f80a276/oas_rails_ui.png)
10
+
5
11
  ## Related Projects
6
12
 
7
13
  - **[ApiPie](https://github.com/Apipie/apipie-rails)**: Doesn't support OAS 3.1, requires learning a DSL, lacks a nice UI
@@ -34,15 +40,15 @@ The goal is to minimize the effort required to create comprehensive documentatio
34
40
 
35
41
  2. Execute:
36
42
 
37
- ```bash
38
- bundle
39
- ```
43
+ ```bash
44
+ bundle
45
+ ```
40
46
 
41
47
  3. Mount the engine in your config/routes.rb file
42
48
 
43
- ```ruby
44
- mount OasRails::Engine => '/docs'
45
- ```
49
+ ```ruby
50
+ mount OasRails::Engine => '/docs'
51
+ ```
46
52
 
47
53
  You'll now have **basic documentation** based on your routes and automatically gathered information at `localhost:3000/docs`. To enhance it, create an initializer file and add [Yard](https://yardoc.org/) tags to your controller methods.
48
54
 
@@ -52,7 +58,7 @@ You'll now have **basic documentation** based on your routes and automatically g
52
58
 
53
59
  You can easy create the initializer file with:
54
60
 
55
- ```
61
+ ```bash
56
62
  rails generate oas_rails:config
57
63
  ```
58
64
 
@@ -60,11 +66,95 @@ Then complete the created file with your data.
60
66
 
61
67
  **Almost every description in a OAS file support simple markdown**
62
68
 
69
+ ## Configuration
70
+
71
+ To configure OasRails, edit the `config/initializers/oas_rails.rb` file. Below are the available configuration options:
72
+
73
+ ### Basic Information about the API
74
+
75
+ - `config.info.title`: The title of your API documentation.
76
+ - `config.info.summary`: A brief summary of your API.
77
+ - `config.info.description`: A detailed description of your API. This can include markdown formatting and will be displayed prominently in your documentation.
78
+ - `config.info.contact.name`: The name of the contact person or organization.
79
+ - `config.info.contact.email`: The contact email address.
80
+ - `config.info.contact.url`: The URL for more information or support.
81
+
82
+ ### Servers Information
83
+
84
+ - `config.servers`: An array of server objects, each containing `url` and `description` keys. For more details, refer to the [OpenAPI Specification](https://spec.openapis.org/oas/latest.html#server-object).
85
+
86
+ ### Tag Information
87
+
88
+ - `config.tags`: An array of tag objects, each containing `name` and `description` keys. For more details, refer to the [OpenAPI Specification](https://spec.openapis.org/oas/latest.html#tag-object).
89
+
90
+ ### Optional Settings
91
+
92
+ - `config.default_tags_from`: Determines the source of default tags for operations. Can be set to `:namespace` or `:controller`.
93
+ - `config.autodiscover_request_body`: Automatically detects request bodies for create/update methods. Default is `true`.
94
+ - `config.autodiscover_responses`: Automatically detects responses from controller renders. Default is `true`.
95
+ - `config.api_path`: Sets the API path if your API is under a different namespace.
96
+
97
+ ### Authentication Settings
98
+
99
+ - `config.authenticate_all_routes_by_default`: Determines whether to authenticate all routes by default. Default is `true`.
100
+ - `config.security_schema`: The default security schema used for authentication. Choose a predefined security schema from `[:api_key_cookie, :api_key_header, :api_key_query, :basic, :bearer, :bearer_jwt, :mutual_tls]`.
101
+ - `config.security_schemas`: Custom security schemas. Follow the [OpenAPI Specification](https://spec.openapis.org/oas/latest.html#security-scheme-object) for defining these schemas.
102
+
103
+ ## Securing the OasRails Engine
104
+
105
+ To secure the OasRails engine, which exposes an endpoint for showing the OAS definition, you can configure authentication to ensure that only authorized users have access. Here are a few methods to achieve this:
106
+
107
+ ### 1. Using Basic Authentication
108
+
109
+ Use basic authentication to protect the OasRails endpoint. You can set this up in an initializer:
110
+
111
+ ```ruby
112
+ # config/initializers/oas_rails.rb
113
+ OasRails::Engine.middleware.use(Rack::Auth::Basic) do |username, password|
114
+ ActiveSupport::SecurityUtils.secure_compare(Rails.application.credentials.oas_rails_username, username) &
115
+ ActiveSupport::SecurityUtils.secure_compare(Rails.application.credentials.oas_rails_password, password)
116
+ end
117
+ ```
118
+
119
+ ### 2. Using Devise's `authenticate` Helper
120
+
121
+ You can use Devise's `authenticate` helper to restrict access to the OasRails endpoint. For example, you can allow only admin users to access the endpoint:
122
+
123
+ ```ruby
124
+ # config/routes.rb
125
+ # ...
126
+ authenticate :user, ->(user) { user.admin? } do
127
+ mount OasRails::Engine, at: '/docs'
128
+ end
129
+ ```
130
+
131
+ ### 3. Custom Authentication
132
+
133
+ To support custom authentication, you can extend the OasRails' ApplicationController using a hook. This allows you to add custom before actions to check for specific user permissions:
134
+
135
+ ```ruby
136
+ # config/initializers/oas_rails.rb
137
+
138
+ ActiveSupport.on_load(:oas_rails_application_controller) do
139
+ # context here is OasRails::ApplicationController
140
+
141
+ before_action do
142
+ raise ActionController::RoutingError.new('Not Found') unless current_user&.admin?
143
+ end
144
+
145
+ def current_user
146
+ # Load the current user
147
+ User.find(session[:user_id]) # Adjust according to your authentication logic
148
+ end
149
+ end
150
+ ```
151
+
63
152
  ## Documenting Your Endpoints
64
153
 
65
154
  Almost every description in an OAS file supports simple markdown. The following tags are available for documenting your endpoints:
66
155
 
67
- ### @summary
156
+ <details>
157
+ <summary style="font-weight: bold; font-size: 1.2em;">@summary</summary>
68
158
 
69
159
  **Structure**: `@summary text`
70
160
 
@@ -73,7 +163,10 @@ Used to add a summary to the endpoint. It replaces the default summary/title of
73
163
  **Example**:
74
164
  `# @summary This endpoint creates a User`
75
165
 
76
- ### @parameter
166
+ </details>
167
+
168
+ <details>
169
+ <summary style="font-weight: bold; font-size: 1.2em;">@parameter</summary>
77
170
 
78
171
  **Structure**: `@parameter name(position) [type] text`
79
172
 
@@ -83,7 +176,10 @@ Represents a parameter for the endpoint. The position can be: `header`, `path`,
83
176
  `# @parameter page(query) [Integer] The page number.`
84
177
  `# @parameter slug(path) [String!] Slug of the Project.`
85
178
 
86
- ### @request_body
179
+ </details>
180
+
181
+ <details>
182
+ <summary style="font-weight: bold; font-size: 1.2em;">@request_body</summary>
87
183
 
88
184
  **Structure**: `@request_body text [type] structure`
89
185
 
@@ -92,7 +188,10 @@ Documents the request body needed by the endpoint. The structure is optional if
92
188
  **Example**:
93
189
  `# @request_body The user to be created [Hash] {user: {name: String, age: Integer, password: String}}`
94
190
 
95
- ### @request_body_example
191
+ </details>
192
+
193
+ <details>
194
+ <summary style="font-weight: bold; font-size: 1.2em;">@request_body_example</summary>
96
195
 
97
196
  **Structure**: `@request_body_example text [type] structure`
98
197
 
@@ -101,7 +200,10 @@ Adds examples to the provided request body.
101
200
  **Example**:
102
201
  `# @request_body_example A complete User. [Hash] {user: {name: 'Luis', age: 30, password: 'MyWeakPassword123'}}`
103
202
 
104
- ### @response
203
+ </details>
204
+
205
+ <details>
206
+ <summary style="font-weight: bold; font-size: 1.2em;">@response</summary>
105
207
 
106
208
  **Structure**: `@response text(code) [type] structure`
107
209
 
@@ -110,7 +212,10 @@ Documents the responses of the endpoint and overrides the default responses foun
110
212
  **Example**:
111
213
  `# @response User not found by the provided Id(404) [Hash] {success: Boolean, message: String}`
112
214
 
113
- ### @tag
215
+ </details>
216
+
217
+ <details>
218
+ <summary style="font-weight: bold; font-size: 1.2em;">@tag</summary>
114
219
 
115
220
  **Structure**: `@tag text`
116
221
 
@@ -119,8 +224,113 @@ Tags your endpoints. You can complete the tag documentation in the initializer f
119
224
  **Example**:
120
225
  `# @tag Users`
121
226
 
227
+ </details>
228
+
229
+ <details>
230
+ <summary style="font-weight: bold; font-size: 1.2em;">@no_auth</summary>
231
+
232
+ **Structure**: `@no_auth`
233
+
234
+ This tag will remove any security requirement from the endpoint. Useful when most of your endpoints require authentication and only a few do not.(Ex: Login, Registration...)
235
+
236
+ **Example**:
237
+ `# @no_auth`
238
+
239
+ </details>
240
+
241
+ <details>
242
+ <summary style="font-weight: bold; font-size: 1.2em;">@auth</summary>
243
+
244
+ **Structure**: `@auth [types]`
245
+
246
+ This tag will set which security mechanisms can be used for the endpoint. The security mechanisms MUST be defined previously in the initializer file.
247
+
248
+ **Example**:
249
+ `# @auth [bearer, basic]`
250
+
251
+ </details>
252
+
122
253
  You can use these tags in your controller methods to enhance the automatically generated documentation. Remember to use markdown formatting in your descriptions for better readability in the generated OAS document.
123
254
 
255
+ ### Example of documented endpoints
256
+
257
+ ```ruby
258
+ class UsersController < ApplicationController
259
+ before_action :set_user, only: %i[show update destroy]
260
+
261
+ # @summary Returns a list of Users.
262
+ #
263
+ # @parameter offset(query) [Integer] Used for pagination of response data (default: 25 items per response). Specifies the offset of the next block of data to receive.
264
+ # @parameter status(query) [Array<String>] Filter by status. (e.g. status[]=inactive&status[]=deleted).
265
+ # @parameter X-front(header) [String] Header for identify the front.
266
+ def index
267
+ @users = User.all
268
+ end
269
+
270
+ # @summary Get a user by id.
271
+ # @auth [bearer]
272
+ #
273
+ # This method show a User by ID. The id must exist of other way it will be returning a **`404`**.
274
+ #
275
+ # @parameter id(path) [Integer] Used for identify the user.
276
+ # @response Requested User(200) [Hash] {user: {name: String, email: String, created_at: DateTime }}
277
+ # @response User not found by the provided Id(404) [Hash] {success: Boolean, message: String}
278
+ # @response You don't have the right permission for access to this resource(403) [Hash] {success: Boolean, message: String}
279
+ def show
280
+ render json: @user
281
+ end
282
+
283
+ # @summary Create a User
284
+ # @no_auth
285
+ #
286
+ # @request_body The user to be created. At least include an `email`. [User!]
287
+ # @request_body_example basic user [Hash] {user: {name: "Luis", email: "luis@gmail.ocom"}}
288
+ def create
289
+ @user = User.new(user_params)
290
+
291
+ if @user.save
292
+ render json: @user, status: :created
293
+ else
294
+ render json: { success: false, errors: @user.errors }, status: :unprocessable_entity
295
+ end
296
+ end
297
+
298
+ # A `user` can be updated with this method
299
+ # - There is no option
300
+ # - It must work
301
+ # @tags users, update
302
+ # @request_body User to be created [Hash] {user: { name: String, email: String, age: Integer}}
303
+ # @request_body_example Update user [Hash] {user: {name: "Luis", email: "luis@gmail.com"}}
304
+ # @request_body_example Complete User [Hash] {user: {name: "Luis", email: "luis@gmail.com", age: 21}}
305
+ def update
306
+ if @user.update(user_params)
307
+ render json: @user
308
+ else
309
+ render json: @user.errors, status: :unprocessable_entity
310
+ end
311
+ end
312
+
313
+ # @summary Delete a User
314
+ # Delete a user and his associated data.
315
+ def destroy
316
+ @user.destroy!
317
+ redirect_to users_url, notice: 'User was successfully destroyed.', status: :see_other
318
+ end
319
+
320
+ private
321
+
322
+ # Use callbacks to share common setup or constraints between actions.
323
+ def set_user
324
+ @user = User.find(params[:id])
325
+ end
326
+
327
+ # Only allow a list of trusted parameters through.
328
+ def user_params
329
+ params.require(:user).permit(:name, :email)
330
+ end
331
+ end
332
+ ```
333
+
124
334
  ## Contributing
125
335
 
126
336
  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!
@@ -134,12 +344,12 @@ Contributions are what make the open source community such an amazing place to l
134
344
  ### Roadmap and Ideas for Improvement
135
345
 
136
346
  - Clean, document and structure the code
137
- - Support documentation of authentication methods
347
+ - [x] Support documentation of authentication methods
138
348
  - Define Global Tags/Configuration (e.g., common responses like 404 errors)
139
349
  - Post-process the JSON and replace common objects with references to components
140
350
  - Create a temporary file with the JSON in production mode to avoid rebuilding it on every request
141
351
  - Create tags for popular gems used in APIs (e.g., a `@pagy` tag for common pagination parameters)
142
- - Add basic authentication to OAS and UI for security reasons
352
+ - [x] Add basic authentication to OAS and UI for security reasons (Solution documented, not need to be managed by the engine)
143
353
  - Implement ability to define OAS by namespaces (e.g., generate OAS for specific routes like `/api` or separate versions V1 and V2)
144
354
 
145
355
  ## License
@@ -1,4 +1,5 @@
1
1
  module OasRails
2
2
  class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
3
4
  end
4
5
  end
@@ -1,9 +1,12 @@
1
1
  module OasRails
2
2
  class OasRailsController < ApplicationController
3
- def index; end
4
-
5
- def oas
6
- render json: Specification.new.to_json, status: :ok
3
+ def index
4
+ respond_to do |format|
5
+ format.html
6
+ format.json do
7
+ render json: Specification.new.to_json, status: :ok
8
+ end
9
+ end
7
10
  end
8
11
  end
9
12
  end
@@ -7,8 +7,6 @@
7
7
  <meta charset="utf-8">
8
8
  <!-- Important: rapi-doc uses utf8 characters -->
9
9
  <script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
10
-
11
- <%= stylesheet_link_tag "oas_rails/application", media: "all" %>
12
10
  </head>
13
11
  <body>
14
12
 
@@ -1 +1 @@
1
- <rapi-doc spec-url = "<%= main_app.oas_rails_path %>/oas" theme = "dark" text-color="#f9f9f9" show-header = 'false'> </rapi-doc>
1
+ <rapi-doc spec-url = "<%= main_app.oas_rails_path %>.json" theme = "dark" text-color="#f9f9f9" show-header = 'false'> </rapi-doc>
data/config/routes.rb CHANGED
@@ -1,4 +1,3 @@
1
1
  OasRails::Engine.routes.draw do
2
- root 'oas_rails#index'
3
- get '/oas', to: 'oas_rails#oas'
2
+ get '(.:format)', to: 'oas_rails#index'
4
3
  end
@@ -1,5 +1,6 @@
1
1
  # config/initializers/oas_rails.rb
2
2
  OasRails.configure do |config|
3
+ # Basic Information about the API
3
4
  config.info.title = 'OasRails'
4
5
  config.info.summary = 'OasRails: Automatic Interactive API Documentation for Rails'
5
6
  config.info.description = <<~HEREDOC
@@ -34,16 +35,55 @@ OasRails.configure do |config|
34
35
  Explore your API documentation and enjoy the power of OasRails!
35
36
 
36
37
  For more information and advanced usage, visit the [OasRails GitHub repository](https://github.com/a-chacon/oas_rails).
37
-
38
-
39
38
  HEREDOC
40
39
  config.info.contact.name = 'a-chacon'
41
40
  config.info.contact.email = 'andres.ch@proton.me'
42
41
  config.info.contact.url = 'https://a-chacon.com'
42
+
43
+ # Servers Information. For more details follow: https://spec.openapis.org/oas/latest.html#server-object
43
44
  config.servers = [{ url: 'http://localhost:3000', description: 'Local' }]
45
+
46
+ # Tag Information. For more details follow: https://spec.openapis.org/oas/latest.html#tag-object
44
47
  config.tags = [{ name: "Users", description: "Manage the `amazing` Users table." }]
45
48
 
46
- # config.default_tags_from = :namespace # Could be: :namespace or :controller
47
- # config.request_body_automatically = true # Try to get request body for create and update methos based on the controller name.
48
- # config.autodiscover_responses = true # Looks for renders in your source code and try to generate the responses.
49
+ # Optional Settings (Uncomment to use)
50
+
51
+ # Extract default tags of operations from namespace or controller. Can be set to :namespace or :controller
52
+ # config.default_tags_from = :namespace
53
+
54
+ # Automatically detect request bodies for create/update methods
55
+ # Default: true
56
+ # config.autodiscover_request_body = false
57
+
58
+ # Automatically detect responses from controller renders
59
+ # Default: true
60
+ # config.autodiscover_responses = false
61
+
62
+ # API path configuration if your API is under a different namespace
63
+ # config.api_path = "/"
64
+
65
+ # #######################
66
+ # Authentication Settings
67
+ # #######################
68
+
69
+ # Whether to authenticate all routes by default
70
+ # Default is true; set to false if you don't want all routes to include secutrity schemas by default
71
+ # config.authenticate_all_routes_by_default = true
72
+
73
+ # Default security schema used for authentication
74
+ # Choose a predefined security schema
75
+ # [:api_key_cookie, :api_key_header, :api_key_query, :basic, :bearer, :bearer_jwt, :mutual_tls]
76
+ # config.security_schema = :bearer
77
+
78
+ # Custom security schemas
79
+ # You can uncomment and modify to use custom security schemas
80
+ # Please follow the documentation: https://spec.openapis.org/oas/latest.html#security-scheme-object
81
+ #
82
+ # config.security_schemas = {
83
+ # bearer:{
84
+ # "type": "apiKey",
85
+ # "name": "api_key",
86
+ # "in": "header"
87
+ # }
88
+ # }
49
89
  end
@@ -1,16 +1,26 @@
1
1
  module OasRails
2
2
  class Configuration
3
- attr_accessor :info, :default_tags_from, :request_body_automatically, :autodiscover_responses
4
- attr_reader :servers, :tags
3
+ attr_accessor :info, :default_tags_from, :autodiscover_request_body, :autodiscover_responses, :api_path, :security_schemas, :authenticate_all_routes_by_default
4
+ attr_reader :servers, :tags, :security_schema
5
5
 
6
- def initialize(**kwargs)
6
+ def initialize
7
7
  @info = Info.new
8
- @servers = kwargs[:servers] || default_servers
8
+ @servers = default_servers
9
9
  @tags = []
10
10
  @swagger_version = '3.1.0'
11
11
  @default_tags_from = "namespace"
12
- @request_body_automatically = true
12
+ @autodiscover_request_body = true
13
13
  @autodiscover_responses = true
14
+ @api_path = "/"
15
+ @authenticate_all_routes_by_default = true
16
+ @security_schema = nil
17
+ @security_schemas = {}
18
+ end
19
+
20
+ def security_schema=(value)
21
+ return unless (security_schema = DEFAULT_SECURITY_SCHEMES[value])
22
+
23
+ @security_schemas = { value => security_schema }
14
24
  end
15
25
 
16
26
  def default_servers
@@ -25,4 +35,45 @@ module OasRails
25
35
  @tags = value.map { |t| Tag.new(name: t[:name], description: t[:description]) }
26
36
  end
27
37
  end
38
+
39
+ DEFAULT_SECURITY_SCHEMES = {
40
+ api_key_cookie: {
41
+ type: "apiKey",
42
+ in: "cookie",
43
+ name: "api_key",
44
+ description: "An API key that will be supplied in a named cookie."
45
+ },
46
+ api_key_header: {
47
+ type: "apiKey",
48
+ in: "header",
49
+ name: "X-API-Key",
50
+ description: "An API key that will be supplied in a named header."
51
+ },
52
+ api_key_query: {
53
+ type: "apiKey",
54
+ in: "query",
55
+ name: "apiKey",
56
+ description: "An API key that will be supplied in a named query parameter."
57
+ },
58
+ basic: {
59
+ type: "http",
60
+ scheme: "basic",
61
+ description: "Basic auth that takes a base64'd combination of `user:password`."
62
+ },
63
+ bearer: {
64
+ type: "http",
65
+ scheme: "bearer",
66
+ description: "A bearer token that will be supplied within an `Authorization` header as `bearer <token>`."
67
+ },
68
+ bearer_jwt: {
69
+ type: "http",
70
+ scheme: "bearer",
71
+ bearerFormat: "JWT",
72
+ 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."
73
+ },
74
+ mutual_tls: {
75
+ type: "mutualTLS",
76
+ description: "Requires a specific mutual TLS certificate to use when making an HTTP request."
77
+ }
78
+ }.freeze
28
79
  end
@@ -21,7 +21,7 @@ module OasRails
21
21
  end
22
22
 
23
23
  def search_for_examples_in_tests(klass:)
24
- case OasRails.detect_test_framework
24
+ case Utils.detect_test_framework
25
25
  when :factory_bot
26
26
  {}
27
27
  # TODO: create examples with FactoryBot
@@ -13,7 +13,8 @@ module OasRails
13
13
  value
14
14
  end
15
15
 
16
- hash[camel_case_key] = processed_value unless (processed_value.is_a?(Hash) || processed_value.is_a?(Array)) && processed_value.empty?
16
+ # hash[camel_case_key] = processed_value unless (processed_value.is_a?(Hash) || processed_value.is_a?(Array)) && processed_value.empty?
17
+ hash[camel_case_key] = processed_value
17
18
  end
18
19
  hash
19
20
  end
@@ -59,7 +59,7 @@ module OasRails
59
59
  schema = {}
60
60
  begin
61
61
  schema = if content.start_with?('{')
62
- OasRails.hash_to_json_schema(parse_hash_structure(content))
62
+ Utils.hash_to_json_schema(parse_hash_structure(content))
63
63
  else
64
64
  # It's likely a variable or method call
65
65
  maybe_a_model, errors = content.gsub('@', "").split(".")
@@ -1,6 +1,6 @@
1
1
  module OasRails
2
2
  class Operation < OasBase
3
- attr_accessor :tags, :summary, :description, :operation_id, :parameters, :method, :docstring, :request_body, :responses
3
+ attr_accessor :tags, :summary, :description, :operation_id, :parameters, :method, :docstring, :request_body, :responses, :security
4
4
 
5
5
  def initialize(method:, summary:, operation_id:, **kwargs)
6
6
  super()
@@ -12,6 +12,7 @@ module OasRails
12
12
  @parameters = kwargs[:parameters] || []
13
13
  @request_body = kwargs[:request_body] || {}
14
14
  @responses = kwargs[:responses] || {}
15
+ @security = kwargs[:security] || []
15
16
  end
16
17
 
17
18
  class << self
@@ -23,7 +24,8 @@ module OasRails
23
24
  parameters = extract_parameters(oas_route:)
24
25
  request_body = extract_request_body(oas_route:)
25
26
  responses = extract_responses(oas_route:)
26
- new(method: oas_route.verb.downcase, summary:, operation_id:, tags:, description:, parameters:, request_body:, responses:)
27
+ security = extract_security(oas_route:)
28
+ new(method: oas_route.verb.downcase, summary:, operation_id:, tags:, description:, parameters:, request_body:, responses:, security:)
27
29
  end
28
30
 
29
31
  def extract_summary(oas_route:)
@@ -89,7 +91,7 @@ module OasRails
89
91
 
90
92
  def extract_request_body(oas_route:)
91
93
  tag_request_body = oas_route.docstring.tags(:request_body).first
92
- if tag_request_body.nil? && OasRails.config.request_body_automatically
94
+ if tag_request_body.nil? && OasRails.config.autodiscover_request_body
93
95
  oas_route.detect_request_body if %w[create update].include? oas_route.method
94
96
  elsif !tag_request_body.nil?
95
97
  RequestBody.from_tags(tag: tag_request_body, examples_tags: oas_route.docstring.tags(:request_body_example))
@@ -112,6 +114,20 @@ module OasRails
112
114
  responses
113
115
  end
114
116
 
117
+ def extract_security(oas_route:)
118
+ return [] if oas_route.docstring.tags(:no_auth).any?
119
+
120
+ if (methods = oas_route.docstring.tags(:auth).first)
121
+ OasRails.config.security_schemas.keys.map { |key| { key => [] } }.select do |schema|
122
+ methods.types.include?(schema.keys.first.to_s)
123
+ end
124
+ elsif OasRails.config.authenticate_all_routes_by_default
125
+ OasRails.config.security_schemas.keys.map { |key| { key => [] } }
126
+ else
127
+ []
128
+ end
129
+ end
130
+
115
131
  def external_docs; end
116
132
  end
117
133
  end
@@ -79,6 +79,7 @@ module OasRails
79
79
  return false if route.defaults[:controller].nil?
80
80
  return false if RAILS_DEFAULT_CONTROLLERS.any? { |default| route.defaults[:controller].start_with?(default) }
81
81
  return false if RAILS_DEFAULT_PATHS.any? { |path| route.path.spec.to_s.include?(path) }
82
+ return false unless route.path.spec.to_s.start_with?(OasRails.config.api_path)
82
83
 
83
84
  true
84
85
  end
@@ -15,28 +15,48 @@ module OasRails
15
15
  {}
16
16
  end
17
17
 
18
+ # Create the Base of the OAS hash.
19
+ # @see https://spec.openapis.org/oas/latest.html#schema
18
20
  def base_spec
19
21
  {
20
22
  openapi: '3.1.0',
21
23
  info: OasRails.config.info.to_spec,
22
24
  servers: OasRails.config.servers.map(&:to_spec),
23
- paths: paths_spec,
24
- components: components_spec,
25
- security: [],
25
+ paths:,
26
+ components:,
27
+ security:,
26
28
  tags: OasRails.config.tags.map(&:to_spec),
27
29
  externalDocs: {}
28
30
  }
29
31
  end
30
32
 
31
- def paths_spec
33
+ # Create the Security Requirement Object.
34
+ # @see https://spec.openapis.org/oas/latest.html#security-requirement-object
35
+ def security
36
+ return [] unless OasRails.config.authenticate_all_routes_by_default
37
+
38
+ OasRails.config.security_schemas.map { |key, _| { key => [] } }
39
+ end
40
+
41
+ # Create the Paths Object For the Root of the OAS.
42
+ # @see https://spec.openapis.org/oas/latest.html#paths-object
43
+ def paths
32
44
  Paths.from_string_paths(string_paths: RouteExtractor.host_paths).to_spec
33
45
  end
34
46
 
35
- def components_spec
47
+ # Created the Components Object For the Root of the OAS.
48
+ # @see https://spec.openapis.org/oas/latest.html#components-object
49
+ def components
36
50
  {
37
- schemas: {}, parameters: {}, securitySchemas: {}, requestBodies: {}, responses: {},
51
+ schemas: {}, parameters: {}, securitySchemes: security_schemas, requestBodies: {}, responses: {},
38
52
  headers: {}, examples: {}, links: {}, callbacks: {}
39
53
  }
40
54
  end
55
+
56
+ # Create the Security Schemas Array inside components field of the OAS.
57
+ # @see https://spec.openapis.org/oas/latest.html#security-scheme-object
58
+ def security_schemas
59
+ OasRails.config.security_schemas
60
+ end
41
61
  end
42
62
  end