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.
- checksums.yaml +4 -4
- data/LICENSE +674 -0
- data/README.md +225 -15
- data/app/controllers/oas_rails/application_controller.rb +1 -0
- data/app/controllers/oas_rails/oas_rails_controller.rb +7 -4
- data/app/views/layouts/oas_rails/application.html.erb +0 -2
- data/app/views/oas_rails/oas_rails/index.html.erb +1 -1
- data/config/routes.rb +1 -2
- data/lib/generators/oas_rails/config/templates/oas_rails_initializer.rb +45 -5
- data/lib/oas_rails/configuration.rb +56 -5
- data/lib/oas_rails/media_type.rb +1 -1
- data/lib/oas_rails/oas_base.rb +2 -1
- data/lib/oas_rails/oas_route.rb +1 -1
- data/lib/oas_rails/operation.rb +19 -3
- data/lib/oas_rails/route_extractor.rb +1 -0
- data/lib/oas_rails/specification.rb +26 -6
- data/lib/oas_rails/utils.rb +66 -0
- data/lib/oas_rails/version.rb +1 -1
- data/lib/oas_rails/yard/oas_yard_factory.rb +3 -3
- data/lib/oas_rails.rb +30 -82
- metadata +12 -21
- data/MIT-LICENSE +0 -20
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,9 +1,12 @@
|
|
1
1
|
module OasRails
|
2
2
|
class OasRailsController < ApplicationController
|
3
|
-
def index
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
@@ -1 +1 @@
|
|
1
|
-
<rapi-doc spec-url = "<%= main_app.oas_rails_path
|
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,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
|
-
#
|
47
|
-
|
48
|
-
#
|
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, :
|
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
|
6
|
+
def initialize
|
7
7
|
@info = Info.new
|
8
|
-
@servers =
|
8
|
+
@servers = default_servers
|
9
9
|
@tags = []
|
10
10
|
@swagger_version = '3.1.0'
|
11
11
|
@default_tags_from = "namespace"
|
12
|
-
@
|
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
|
data/lib/oas_rails/media_type.rb
CHANGED
data/lib/oas_rails/oas_base.rb
CHANGED
@@ -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
|
data/lib/oas_rails/oas_route.rb
CHANGED
@@ -59,7 +59,7 @@ module OasRails
|
|
59
59
|
schema = {}
|
60
60
|
begin
|
61
61
|
schema = if content.start_with?('{')
|
62
|
-
|
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(".")
|
data/lib/oas_rails/operation.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
24
|
-
components
|
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
|
-
|
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
|
-
|
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: {},
|
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
|