swagger_api 0.1.43
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +20 -0
- data/.gitignore +13 -0
- data/.gitlab-ci.yml +61 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +155 -0
- data/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/swagger_api.rb +123 -0
- data/lib/swagger_api/actions.rb +43 -0
- data/lib/swagger_api/column_schema.rb +41 -0
- data/lib/swagger_api/component_schema.rb +50 -0
- data/lib/swagger_api/components.rb +24 -0
- data/lib/swagger_api/concerns/columns.rb +24 -0
- data/lib/swagger_api/operations/base.rb +86 -0
- data/lib/swagger_api/operations/create.rb +39 -0
- data/lib/swagger_api/operations/delete.rb +20 -0
- data/lib/swagger_api/operations/index.rb +50 -0
- data/lib/swagger_api/operations/show.rb +11 -0
- data/lib/swagger_api/operations/update.rb +32 -0
- data/lib/swagger_api/paths.rb +52 -0
- data/lib/swagger_api/railtie.rb +6 -0
- data/lib/swagger_api/request_bodies.rb +28 -0
- data/lib/swagger_api/tasks/swagger.rake +13 -0
- data/lib/swagger_api/version.rb +3 -0
- data/swagger.yml +46 -0
- data/swagger_api.gemspec +38 -0
- metadata +162 -0
data/bin/setup
ADDED
data/lib/swagger_api.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'swagger_api/version'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module SwaggerApi
|
5
|
+
class Generator
|
6
|
+
def prettify
|
7
|
+
JSON.pretty_generate(JSON.parse(json))
|
8
|
+
end
|
9
|
+
|
10
|
+
def json
|
11
|
+
create.to_json
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
@config ||= {
|
16
|
+
openapi: '3.0.0',
|
17
|
+
security: [{api_key: []}],
|
18
|
+
info: info,
|
19
|
+
servers: [{url: server_url}],
|
20
|
+
paths: Paths.new(controllers: config.controllers).create,
|
21
|
+
components: {
|
22
|
+
responses: responses,
|
23
|
+
schemas: Components.new(controllers: config.controllers).create,
|
24
|
+
requestBodies: RequestBodies.new(controllers: config.controllers).create,
|
25
|
+
securitySchemes: security_schemes,
|
26
|
+
},
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def config
|
31
|
+
@yaml_config ||= JSON.parse(YAML.load_file("#{Rails.root.to_s}/config/swagger.yml").to_json, object_class: OpenStruct)
|
32
|
+
end
|
33
|
+
|
34
|
+
def security_schemes
|
35
|
+
{
|
36
|
+
api_key: {
|
37
|
+
type: 'apiKey',
|
38
|
+
name: 'Authorization',
|
39
|
+
in: 'header'
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def responses
|
45
|
+
@responses ||= custom_responses || default_responses
|
46
|
+
end
|
47
|
+
|
48
|
+
def custom_responses
|
49
|
+
custom_responses_path = "#{Rails.root.to_s}/config/swagger_custom_responses.json"
|
50
|
+
return unless File.exist?(custom_responses_path)
|
51
|
+
@custom_responses ||= JSON.parse(File.read(custom_responses_path), object_class: OpenStruct)
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_responses
|
55
|
+
{
|
56
|
+
NotFound: {
|
57
|
+
description: "The specified resource was not found",
|
58
|
+
content: {
|
59
|
+
'application/json; charset=utf-8' => {
|
60
|
+
schema: {
|
61
|
+
type: 'string',
|
62
|
+
example: 'Not Found'
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
},
|
67
|
+
Unauthorized: {
|
68
|
+
description: "Unauthorized",
|
69
|
+
content: {
|
70
|
+
'application/json; charset=utf-8' => {
|
71
|
+
schema: {
|
72
|
+
type: 'string',
|
73
|
+
example: 'Not Authorized'
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
},
|
78
|
+
BadRequest: {
|
79
|
+
description: "Bad Request",
|
80
|
+
content: {
|
81
|
+
'application/json; charset=utf-8' => {
|
82
|
+
schema: {
|
83
|
+
example: ['The field name is invalid.', 'The id must be present'],
|
84
|
+
type: 'array',
|
85
|
+
items: {
|
86
|
+
type: 'string'
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def info
|
96
|
+
{
|
97
|
+
version: config.info.version,
|
98
|
+
title: config.info.title,
|
99
|
+
description: config.info.description,
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def server_url
|
104
|
+
return unless config.server.respond_to?(Rails.env)
|
105
|
+
config.servers.send(Rails.env).url
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
require 'swagger_api/railtie'
|
110
|
+
|
111
|
+
require 'swagger_api/actions'
|
112
|
+
require 'swagger_api/concerns/columns'
|
113
|
+
require 'swagger_api/column_schema'
|
114
|
+
require 'swagger_api/component_schema'
|
115
|
+
require 'swagger_api/components'
|
116
|
+
require 'swagger_api/operations/base'
|
117
|
+
require 'swagger_api/operations/create'
|
118
|
+
require 'swagger_api/operations/delete'
|
119
|
+
require 'swagger_api/operations/index'
|
120
|
+
require 'swagger_api/operations/show'
|
121
|
+
require 'swagger_api/operations/update'
|
122
|
+
require 'swagger_api/paths'
|
123
|
+
require 'swagger_api/request_bodies'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'active_attr'
|
2
|
+
|
3
|
+
module SwaggerApi
|
4
|
+
class Actions
|
5
|
+
include ActiveAttr::Model
|
6
|
+
|
7
|
+
validate :validate_actions
|
8
|
+
attr_accessor :controller
|
9
|
+
|
10
|
+
|
11
|
+
def all!
|
12
|
+
raise self.errors.full_messages unless self.valid?
|
13
|
+
return only_actions unless only_actions.blank?
|
14
|
+
defined_actions
|
15
|
+
end
|
16
|
+
|
17
|
+
def defined_actions
|
18
|
+
(restful_actions + controller_actions) - except_actions
|
19
|
+
end
|
20
|
+
|
21
|
+
def controller_actions
|
22
|
+
return [] unless controller.actions.is_a? Array
|
23
|
+
controller.actions
|
24
|
+
end
|
25
|
+
|
26
|
+
def only_actions
|
27
|
+
controller.actions.try(:only) || []
|
28
|
+
end
|
29
|
+
|
30
|
+
def except_actions
|
31
|
+
controller.actions.try(:except) || []
|
32
|
+
end
|
33
|
+
|
34
|
+
def restful_actions
|
35
|
+
%w(index show create update delete)
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_actions
|
39
|
+
errors.add(:base, "`actions` must include at least one of #{restful_actions}") if restful_actions.blank?
|
40
|
+
errors.add(:base, "`actions` can only include one of #{restful_actions}. #{(defined_actions - restful_actions)} are not allowed") unless (defined_actions - restful_actions).blank?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SwaggerApi
|
2
|
+
class ColumnSchema
|
3
|
+
include ActiveAttr::Model
|
4
|
+
attr_accessor :column
|
5
|
+
|
6
|
+
def create
|
7
|
+
schema = {
|
8
|
+
type: type_from_column,
|
9
|
+
format: format_from_column
|
10
|
+
}
|
11
|
+
schema[:minimum] = 1 if column.type == :integer && column.name.to_s.ends_with?('id')
|
12
|
+
schema[:minimum] = 0 if column.type == :integer && !column.name.to_s.ends_with?('id')
|
13
|
+
schema
|
14
|
+
end
|
15
|
+
|
16
|
+
def type_from_column
|
17
|
+
if %i(datetime date time).include?(column.type)
|
18
|
+
:string
|
19
|
+
elsif %i(float double).include?(column.type)
|
20
|
+
:number
|
21
|
+
else
|
22
|
+
column.type
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_from_column
|
27
|
+
case column.type
|
28
|
+
when :datetime
|
29
|
+
'date-time'
|
30
|
+
when :integer
|
31
|
+
:int64
|
32
|
+
else
|
33
|
+
if column.name.to_s == 'email'
|
34
|
+
:email
|
35
|
+
else
|
36
|
+
column.type
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module SwaggerApi
|
2
|
+
class ComponentSchema
|
3
|
+
include ActiveAttr::Model
|
4
|
+
include Concerns::Columns
|
5
|
+
attr_accessor :controller
|
6
|
+
|
7
|
+
def create
|
8
|
+
{
|
9
|
+
required: required,
|
10
|
+
properties: properties,
|
11
|
+
type: 'object'
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def required
|
18
|
+
controller.try(:columns).try(:required) ||
|
19
|
+
columns.map(&:name) & clean_required_attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
def clean_required_attributes
|
23
|
+
required_attributes.map do |attribute|
|
24
|
+
if model.reflect_on_all_associations(:belongs_to).map(&:name).map(&:to_s).include?(attribute)
|
25
|
+
"#{attribute}_id"
|
26
|
+
else
|
27
|
+
attribute
|
28
|
+
end
|
29
|
+
end.compact.uniq.map(&:to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def required_attributes
|
33
|
+
model.validators.map do |validator|
|
34
|
+
validator.attributes if validator.is_a?(ActiveRecord::Validations::PresenceValidator)
|
35
|
+
end.compact.flatten.uniq.map(&:to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
def properties
|
39
|
+
properties = {}
|
40
|
+
columns.each do |column|
|
41
|
+
properties[column.name] = ColumnSchema.new(column: column).create
|
42
|
+
end
|
43
|
+
properties
|
44
|
+
end
|
45
|
+
|
46
|
+
def model
|
47
|
+
controller.model.constantize
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SwaggerApi
|
2
|
+
class Components
|
3
|
+
include ActiveAttr::Model
|
4
|
+
attr_accessor :controllers
|
5
|
+
|
6
|
+
def create
|
7
|
+
return @components unless @components.nil?
|
8
|
+
@components = {}
|
9
|
+
controllers.each do |controller|
|
10
|
+
if controller.custom_model_file.nil?
|
11
|
+
@components[controller.model] = ComponentSchema.new(controller: controller).create
|
12
|
+
else
|
13
|
+
@components.merge!(custom_json(controller.custom_model_file))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
@components
|
17
|
+
end
|
18
|
+
|
19
|
+
def custom_json(custom_model_file)
|
20
|
+
file = File.read(custom_model_file)
|
21
|
+
JSON.parse(file)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SwaggerApi
|
2
|
+
module Concerns
|
3
|
+
module Columns
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
def columns
|
8
|
+
if controller.columns.try(:only).present?
|
9
|
+
model.columns.select! do |column|
|
10
|
+
controller.columns.only.include?(column.name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
if controller.columns.try(:except).present?
|
15
|
+
model.columns.reject! do |column|
|
16
|
+
controller.columns.except.include?(column.name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
model.columns
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module SwaggerApi
|
2
|
+
module Operations
|
3
|
+
class Base
|
4
|
+
include ActiveAttr::Model
|
5
|
+
include SwaggerApi::Concerns::Columns
|
6
|
+
|
7
|
+
attr_accessor :controller, :action
|
8
|
+
|
9
|
+
def create
|
10
|
+
{
|
11
|
+
summary: "#{readable_action} #{model_name}",
|
12
|
+
description: "#{readable_action} #{model_name.downcase}'s information",
|
13
|
+
parameters: parameters,
|
14
|
+
responses: responses,
|
15
|
+
tags: [model_name],
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def parameters
|
20
|
+
[
|
21
|
+
{
|
22
|
+
name: 'id',
|
23
|
+
in: 'path',
|
24
|
+
description: "ID of #{model_name}",
|
25
|
+
required: true,
|
26
|
+
schema: {
|
27
|
+
type: :integer,
|
28
|
+
format: :int64,
|
29
|
+
minimum: 1
|
30
|
+
},
|
31
|
+
}
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
def responses
|
36
|
+
return @responses unless @responses.nil?
|
37
|
+
@responses = success_response
|
38
|
+
error_responses.each do |error_response|
|
39
|
+
@responses.merge!(error_response)
|
40
|
+
end
|
41
|
+
@responses
|
42
|
+
end
|
43
|
+
|
44
|
+
def success_response
|
45
|
+
{
|
46
|
+
'200' => {
|
47
|
+
description: "#{readable_action} #{model_name.downcase}'s information",
|
48
|
+
content: {
|
49
|
+
'application/json; charset=utf-8' => {
|
50
|
+
schema: {
|
51
|
+
'$ref' => "#/components/schemas/#{model_name}"
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def error_responses
|
60
|
+
[
|
61
|
+
{
|
62
|
+
'404' => { '$ref' => '#/components/responses/NotFound' }
|
63
|
+
},
|
64
|
+
{
|
65
|
+
'401' => { '$ref' => '#/components/responses/Unauthorized' }
|
66
|
+
},
|
67
|
+
{
|
68
|
+
'422' => { '$ref' => '#/components/responses/BadRequest' }
|
69
|
+
}
|
70
|
+
]
|
71
|
+
end
|
72
|
+
|
73
|
+
def readable_action
|
74
|
+
@readable_action ||= self.class.name.demodulize.downcase
|
75
|
+
end
|
76
|
+
|
77
|
+
def model_name
|
78
|
+
@model_name ||= controller.model
|
79
|
+
end
|
80
|
+
|
81
|
+
def model
|
82
|
+
@model ||= model_name.constantize
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SwaggerApi
|
2
|
+
module Operations
|
3
|
+
class Create < Base
|
4
|
+
def create
|
5
|
+
create = super
|
6
|
+
create[:requestBody] = request_body
|
7
|
+
create.delete(:parameters)
|
8
|
+
create
|
9
|
+
end
|
10
|
+
|
11
|
+
def request_body
|
12
|
+
{
|
13
|
+
"$ref" => "#/components/requestBodies/#{model_name}"
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def success_response
|
18
|
+
{
|
19
|
+
'303' => {
|
20
|
+
description: "#{readable_action} #{model_name.downcase}'s information",
|
21
|
+
headers: {
|
22
|
+
Location: {
|
23
|
+
schema: {
|
24
|
+
type: :string
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def error_responses
|
33
|
+
super.reject do |error_response|
|
34
|
+
%w(404).include?(error_response.keys.first)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|