swagger-api 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16f8848d68d8e83e8d88c3e6bbf3974c79c1f85149ebe59b7e208ab22b78524d
4
- data.tar.gz: f55a8a8a95c9097ba09b0312eb61dd4eb0aa10f508e70fcfbfaab619ef0ca2f3
3
+ metadata.gz: 1a6147e6c08e3b29f19ed4955112635e165c11ae6f922d293125c51b8e8b6e9b
4
+ data.tar.gz: 41d314a1924914c9acd27e1f6cda6e12fcf9f18015bfa8d15921cf5b2fa00f06
5
5
  SHA512:
6
- metadata.gz: 4fabfda39fd77b0302505de767de179db91cdcb88f5b45d2423021284499b8acc04430ce4fb314443e0a742f60c8642438199eb438bad1234cad4e41f7d0752d
7
- data.tar.gz: 8dd7a9ac27ed97a88303dac2d2cc49a52c368f34e2c8eac6fd8bed07f32b3d00367ac186c5a4e05fa73e3056930512eaf0edb0e7f1e299e00af5ea13e5059a7c
6
+ metadata.gz: d8c3bd825606f6d379858a2df667682acf21dfbb7186b703df52278271775004750493fc88c096e484b078a86b96a5cc9ce4202fe55e29e3a7a7e72a53d8fd17
7
+ data.tar.gz: e83972c44bdda154f3807427c9b5c18ec138cbc645324f42ee4c1e5948feb30da54ce4572e4b65c9bb9832847e0714b4d2d505b6ffb29645565b3a37b283d75c
data/Gemfile.lock ADDED
@@ -0,0 +1,134 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ swagger-api (0.1.1)
5
+ rails (>= 5.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (5.1.6)
11
+ actionpack (= 5.1.6)
12
+ nio4r (~> 2.0)
13
+ websocket-driver (~> 0.6.1)
14
+ actionmailer (5.1.6)
15
+ actionpack (= 5.1.6)
16
+ actionview (= 5.1.6)
17
+ activejob (= 5.1.6)
18
+ mail (~> 2.5, >= 2.5.4)
19
+ rails-dom-testing (~> 2.0)
20
+ actionpack (5.1.6)
21
+ actionview (= 5.1.6)
22
+ activesupport (= 5.1.6)
23
+ rack (~> 2.0)
24
+ rack-test (>= 0.6.3)
25
+ rails-dom-testing (~> 2.0)
26
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
27
+ actionview (5.1.6)
28
+ activesupport (= 5.1.6)
29
+ builder (~> 3.1)
30
+ erubi (~> 1.4)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
33
+ activejob (5.1.6)
34
+ activesupport (= 5.1.6)
35
+ globalid (>= 0.3.6)
36
+ activemodel (5.1.6)
37
+ activesupport (= 5.1.6)
38
+ activerecord (5.1.6)
39
+ activemodel (= 5.1.6)
40
+ activesupport (= 5.1.6)
41
+ arel (~> 8.0)
42
+ activesupport (5.1.6)
43
+ concurrent-ruby (~> 1.0, >= 1.0.2)
44
+ i18n (>= 0.7, < 2)
45
+ minitest (~> 5.1)
46
+ tzinfo (~> 1.1)
47
+ arel (8.0.0)
48
+ builder (3.2.3)
49
+ concurrent-ruby (1.0.5)
50
+ crass (1.0.4)
51
+ diff-lcs (1.3)
52
+ erubi (1.7.1)
53
+ globalid (0.4.1)
54
+ activesupport (>= 4.2.0)
55
+ i18n (1.0.0)
56
+ concurrent-ruby (~> 1.0)
57
+ loofah (2.2.2)
58
+ crass (~> 1.0.2)
59
+ nokogiri (>= 1.5.9)
60
+ mail (2.7.0)
61
+ mini_mime (>= 0.1.1)
62
+ method_source (0.9.0)
63
+ mini_mime (1.0.0)
64
+ mini_portile2 (2.3.0)
65
+ minitest (5.11.3)
66
+ nio4r (2.3.0)
67
+ nokogiri (1.8.2)
68
+ mini_portile2 (~> 2.3.0)
69
+ rack (2.0.4)
70
+ rack-test (1.0.0)
71
+ rack (>= 1.0, < 3)
72
+ rails (5.1.6)
73
+ actioncable (= 5.1.6)
74
+ actionmailer (= 5.1.6)
75
+ actionpack (= 5.1.6)
76
+ actionview (= 5.1.6)
77
+ activejob (= 5.1.6)
78
+ activemodel (= 5.1.6)
79
+ activerecord (= 5.1.6)
80
+ activesupport (= 5.1.6)
81
+ bundler (>= 1.3.0)
82
+ railties (= 5.1.6)
83
+ sprockets-rails (>= 2.0.0)
84
+ rails-dom-testing (2.0.3)
85
+ activesupport (>= 4.2.0)
86
+ nokogiri (>= 1.6)
87
+ rails-html-sanitizer (1.0.4)
88
+ loofah (~> 2.2, >= 2.2.2)
89
+ railties (5.1.6)
90
+ actionpack (= 5.1.6)
91
+ activesupport (= 5.1.6)
92
+ method_source
93
+ rake (>= 0.8.7)
94
+ thor (>= 0.18.1, < 2.0)
95
+ rake (10.5.0)
96
+ rspec (3.7.0)
97
+ rspec-core (~> 3.7.0)
98
+ rspec-expectations (~> 3.7.0)
99
+ rspec-mocks (~> 3.7.0)
100
+ rspec-core (3.7.1)
101
+ rspec-support (~> 3.7.0)
102
+ rspec-expectations (3.7.0)
103
+ diff-lcs (>= 1.2.0, < 2.0)
104
+ rspec-support (~> 3.7.0)
105
+ rspec-mocks (3.7.0)
106
+ diff-lcs (>= 1.2.0, < 2.0)
107
+ rspec-support (~> 3.7.0)
108
+ rspec-support (3.7.1)
109
+ sprockets (3.7.1)
110
+ concurrent-ruby (~> 1.0)
111
+ rack (> 1, < 3)
112
+ sprockets-rails (3.2.1)
113
+ actionpack (>= 4.0)
114
+ activesupport (>= 4.0)
115
+ sprockets (>= 3.0.0)
116
+ thor (0.20.0)
117
+ thread_safe (0.3.6)
118
+ tzinfo (1.2.5)
119
+ thread_safe (~> 0.1)
120
+ websocket-driver (0.6.5)
121
+ websocket-extensions (>= 0.1.0)
122
+ websocket-extensions (0.1.3)
123
+
124
+ PLATFORMS
125
+ ruby
126
+
127
+ DEPENDENCIES
128
+ bundler (~> 1.16)
129
+ rake (~> 10.0)
130
+ rspec (~> 3.0)
131
+ swagger-api!
132
+
133
+ BUNDLED WITH
134
+ 1.16.1
data/lib/swagger/api.rb CHANGED
@@ -1,14 +1,7 @@
1
- module Swagger
2
- class Api
3
- include ActiveAttr::Model
4
- include ActiveModel::Validations
5
- attribute :openapi, type: String, default: '3.0.0'
6
- attribute :servers, default: []
7
- attribute :info, type: Object
8
- attribute :security, default: [{api_key: []}]
9
- attribute :paths, default: []
10
- attribute :components, default: {}
1
+ require 'swagger/api/version'
11
2
 
3
+ module Swagger
4
+ module Api
12
5
  def self.prettify
13
6
  JSON.pretty_generate(JSON.parse(json))
14
7
  end
@@ -18,17 +11,17 @@ module Swagger
18
11
  end
19
12
 
20
13
  def self.create
21
- @config ||= Swagger::Api.new(
14
+ @config ||= {
22
15
  info: info,
23
16
  servers: [{url: server_url}],
24
17
  paths: Paths.new(controllers: config.controllers).create,
25
18
  components: {
26
19
  responses: responses,
27
20
  schemas: Components.new(controllers: config.controllers).create,
28
- requestBodies: Swagger::RequestBodies.new(controllers: config.controllers).create,
21
+ requestBodies: RequestBodies.new(controllers: config.controllers).create,
29
22
  securitySchemes: security_schemes,
30
23
  },
31
- )
24
+ }
32
25
  end
33
26
 
34
27
  def self.config
@@ -110,3 +103,16 @@ module Swagger
110
103
  end
111
104
  end
112
105
  end
106
+ require 'swagger/api/actions'
107
+ require 'swagger/api/concerns/columns'
108
+ require 'swagger/api/column_schema'
109
+ require 'swagger/api/component_schema'
110
+ require 'swagger/api/components'
111
+ require 'swagger/api/operations/base'
112
+ require 'swagger/api/operations/create'
113
+ require 'swagger/api/operations/delete'
114
+ require 'swagger/api/operations/index'
115
+ require 'swagger/api/operations/show'
116
+ require 'swagger/api/operations/update'
117
+ require 'swagger/api/paths'
118
+ require 'swagger/api/request_bodies'
@@ -0,0 +1,43 @@
1
+ module Swagger
2
+ module Api
3
+ class Actions
4
+ include ActiveAttr::Model
5
+
6
+ validate :validate_actions
7
+ attr_accessor :controller
8
+
9
+
10
+ def all!
11
+ raise self.errors.full_messages unless self.valid?
12
+ return only_actions unless only_actions.blank?
13
+ defined_actions
14
+ end
15
+
16
+ def defined_actions
17
+ (restful_actions + controller_actions) - except_actions
18
+ end
19
+
20
+ def controller_actions
21
+ return [] unless controller.actions.is_a? Array
22
+ controller.actions
23
+ end
24
+
25
+ def only_actions
26
+ controller.actions.try(:only) || []
27
+ end
28
+
29
+ def except_actions
30
+ controller.actions.try(:except) || []
31
+ end
32
+
33
+ def restful_actions
34
+ %w(index show create update delete)
35
+ end
36
+
37
+ def validate_actions
38
+ errors.add(:base, "`actions` must include at least one of #{restful_actions}") if restful_actions.blank?
39
+ errors.add(:base, "`actions` can only include one of #{restful_actions}. #{(defined_actions - restful_actions)} are not allowed") unless (defined_actions - restful_actions).blank?
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ module Swagger
2
+ module Api
3
+ class ColumnSchema
4
+ include ActiveAttr::Model
5
+ attr_accessor :column
6
+
7
+ def create
8
+ schema = {
9
+ type: type_from_column,
10
+ format: format_from_column
11
+ }
12
+ schema[:minimum] = 1 if column.type == :integer && column.name.to_s.ends_with?('id')
13
+ schema[:minimum] = 0 if column.type == :integer && !column.name.to_s.ends_with?('id')
14
+ schema
15
+ end
16
+
17
+ def type_from_column
18
+ if %i(datetime date time).include?(column.type)
19
+ :string
20
+ elsif %i(float double).include?(column.type)
21
+ :number
22
+ else
23
+ column.type
24
+ end
25
+ end
26
+
27
+ def format_from_column
28
+ case column.type
29
+ when :datetime
30
+ 'date-time'
31
+ when :integer
32
+ :int64
33
+ else
34
+ if column.name.to_s == 'email'
35
+ :email
36
+ else
37
+ column.type
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,52 @@
1
+ module Swagger
2
+ module Api
3
+ class ComponentSchema
4
+ include ActiveAttr::Model
5
+ include Concerns::Columns
6
+ attr_accessor :controller
7
+
8
+ def create
9
+ {
10
+ required: required,
11
+ properties: properties,
12
+ type: 'object'
13
+ }
14
+ end
15
+
16
+ private
17
+
18
+ def required
19
+ controller.try(:columns).try(:required) ||
20
+ columns.map(&:name) & clean_required_attributes
21
+ end
22
+
23
+ def clean_required_attributes
24
+ required_attributes.map do |attribute|
25
+ if model.reflect_on_all_associations(:belongs_to).map(&:name).map(&:to_s).include?(attribute)
26
+ "#{attribute}_id"
27
+ else
28
+ attribute
29
+ end
30
+ end.compact.uniq.map(&:to_s)
31
+ end
32
+
33
+ def required_attributes
34
+ model.validators.map do |validator|
35
+ validator.attributes if validator.is_a?(ActiveRecord::Validations::PresenceValidator)
36
+ end.compact.flatten.uniq.map(&:to_s)
37
+ end
38
+
39
+ def properties
40
+ properties = {}
41
+ columns.each do |column|
42
+ properties[column.name] = ColumnSchema.new(column: column).create
43
+ end
44
+ properties
45
+ end
46
+
47
+ def model
48
+ controller.model.constantize
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,16 @@
1
+ module Swagger
2
+ module Api
3
+ class Components
4
+ include ActiveAttr::Model
5
+ attr_accessor :controllers
6
+
7
+ def create
8
+ components = {}
9
+ controllers.each do |controller|
10
+ components[controller.model] = ComponentSchema.new(controller: controller).create
11
+ end
12
+ components
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module Swagger
2
+ module Api
3
+ module Concerns
4
+ module Columns
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def columns
9
+ if controller.columns.try(:only).present?
10
+ model.columns.select! do |column|
11
+ controller.columns.only.include?(column.name)
12
+ end
13
+ end
14
+
15
+ if controller.columns.try(:except).present?
16
+ model.columns.reject! do |column|
17
+ controller.columns.except.include?(column.name)
18
+ end
19
+ end
20
+ model.columns
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,92 @@
1
+ module Swagger
2
+ module Api
3
+ module Operations
4
+ class Base
5
+ include ActiveAttr::Model
6
+ include Swagger::Api::Concerns::Columns
7
+
8
+ attr_accessor :controller, :action
9
+
10
+ def create
11
+ {
12
+ summary: "#{readable_action} #{model_name}",
13
+ description: "#{readable_action} #{model_name.downcase}'s information",
14
+ parameters: parameters,
15
+ responses: responses,
16
+ tags: [model_name]
17
+
18
+ }
19
+ end
20
+
21
+ def parameters
22
+ [
23
+ {
24
+ name: 'id',
25
+ in: 'path',
26
+ description: "ID of #{model_name}",
27
+ required: true,
28
+ schema: {
29
+ type: :integer,
30
+ format: :int64,
31
+ minimum: 1
32
+ },
33
+ }
34
+ ]
35
+ end
36
+
37
+ def responses
38
+ return @responses unless @responses.nil?
39
+ @responses = success_response
40
+ error_responses.each do |error_response|
41
+ @responses.merge!(error_response)
42
+ end
43
+ @responses
44
+ end
45
+
46
+ def success_response
47
+ {
48
+ '200' => {
49
+ description: "#{readable_action} #{model_name.downcase}'s information",
50
+ content: {
51
+ 'application/json; charset=utf-8' => {
52
+ schema: {
53
+ '$ref' => "#/components/schemas/#{model_name}"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ end
60
+
61
+ def error_responses
62
+ [
63
+ {
64
+ '404' => { '$ref' => '#/components/responses/NotFound' }
65
+ },
66
+ {
67
+ '401' => { '$ref' => '#/components/responses/Unauthorized' }
68
+ },
69
+ {
70
+ '422' => { '$ref' => '#/components/responses/BadRequest' }
71
+ },
72
+ {
73
+ '500' => { '$ref' => '#/components/responses/Unexpected' }
74
+ }
75
+ ]
76
+ end
77
+
78
+ def readable_action
79
+ @readable_action ||= self.class.name.demodulize.downcase
80
+ end
81
+
82
+ def model_name
83
+ @model_name ||= controller.model
84
+ end
85
+
86
+ def model
87
+ @model ||= model_name.constantize
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,27 @@
1
+ module Swagger
2
+ module Api
3
+ module Operations
4
+ class Create < Base
5
+
6
+ def create
7
+ create = super
8
+ create[:requestBody] = request_body
9
+ create.delete(:parameters)
10
+ create
11
+ end
12
+
13
+ def request_body
14
+ {
15
+ "$ref" => "#/components/requestBodies/#{model_name}"
16
+ }
17
+ end
18
+
19
+ def error_responses
20
+ super.reject do |error_response|
21
+ error_response.keys.first == '404'
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ module Swagger
2
+ module Api
3
+ module Operations
4
+ class Delete < Base
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ module Swagger
2
+ module Api
3
+ module Operations
4
+ class Index < Base
5
+ def parameters
6
+ columns.map do |column|
7
+ {
8
+ name: column.name,
9
+ in: 'query',
10
+ required: false,
11
+ description: "#{column.name} of #{model.name}",
12
+ schema: Swagger::Api::ColumnSchema.new(column: column).create,
13
+ }
14
+ end
15
+ end
16
+
17
+ def readable_action
18
+ 'list'
19
+ end
20
+
21
+ def error_responses
22
+ super.reject do |error_response|
23
+ %w(404).include?(error_response.keys.first)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ module Swagger
2
+ module Api
3
+ module Operations
4
+ class Show < Base
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ module Swagger
2
+ module Api
3
+ module Operations
4
+ class Update < Base
5
+ def create
6
+ create = super
7
+ create[:requestBody] = request_body
8
+ create
9
+ end
10
+
11
+ def request_body
12
+ {
13
+ "$ref" => "#/components/requestBodies/#{model_name}"
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ module Swagger
2
+ module Api
3
+ class Paths
4
+ include ActiveAttr::Model
5
+ attr_accessor :controllers
6
+
7
+ def create
8
+ return @paths unless @paths.nil?
9
+ @paths = {}
10
+ controllers.each do |controller|
11
+ if controller.custom_path_file.nil?
12
+ @paths.merge!(paths(controller))
13
+ else
14
+ @paths.merge!(custom_json(controller))
15
+ end
16
+ end
17
+ @paths
18
+ end
19
+
20
+ private
21
+
22
+ def paths(controller)
23
+ paths = {}
24
+ paths["#{route(controller)}"] = {}
25
+ paths["#{route(controller)}{id}/"] = {}
26
+ paths["#{route(controller)}"][:get] = Swagger::Api::Operations::Index.new(controller: controller).create if action?(controller, 'index')
27
+ paths["#{route(controller)}"][:post] = Swagger::Api::Operations::Create.new(controller: controller).create if action?(controller, 'create')
28
+ paths["#{route(controller)}{id}/"][:get] = Swagger::Api::Operations::Show.new(controller: controller).create if action?(controller, 'show')
29
+ paths["#{route(controller)}{id}/"][:put] = Swagger::Api::Operations::Update.new(controller: controller).create if action?(controller, 'update')
30
+ paths["#{route(controller)}{id}/"][:delete] = Swagger::Api::Operations::Delete.new(controller: controller).create if action?(controller, 'delete')
31
+ paths["#{route(controller)}"].delete if paths["#{route(controller)}"].blank?
32
+ paths["#{route(controller)}{id}/"].delete if paths["#{route(controller)}{id}/"].blank?
33
+ paths
34
+ end
35
+
36
+ def route(controller)
37
+ "/#{controller.name.demodulize.underscore.gsub('_controller', '')}/"
38
+ end
39
+
40
+ def custom_json(controller)
41
+ file = File.read(controller.custom_path_file)
42
+ JSON.parse(file)
43
+ end
44
+
45
+ def actions(controller)
46
+ Swagger::Api::Actions.new(controller: controller).all!
47
+ end
48
+
49
+ def action?(controller, action_name)
50
+ actions(controller).include?(action_name)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,30 @@
1
+ module Swagger
2
+ module Api
3
+ class RequestBodies
4
+ include ActiveAttr::Model
5
+ attr_accessor :controllers
6
+
7
+ def create
8
+ request_bodies = {}
9
+ controllers.each do |controller|
10
+ request_bodies[controller.model] = request_body(controller)
11
+ end
12
+ request_bodies
13
+ end
14
+
15
+ def request_body(controller)
16
+ {
17
+ content: {
18
+ 'application/json' => {
19
+ schema: {
20
+ '$ref' => "#/components/schemas/#{controller.model}"
21
+ }
22
+ }
23
+ },
24
+ description: "audience attribute",
25
+ required: true
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  module Swagger
2
2
  module Api
3
- VERSION = "0.1.1"
3
+ VERSION = "0.1.2"
4
4
  end
5
5
  end
data/swagger-api.gemspec CHANGED
@@ -33,4 +33,5 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency "bundler", "~> 1.16"
34
34
  spec.add_development_dependency "rake", "~> 10.0"
35
35
  spec.add_development_dependency "rspec", "~> 3.0"
36
+ spec.add_dependency "rails", ">= 5.0"
36
37
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swagger-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Full Measure Education
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
55
69
  description: A simple library for autocreating RESTful swagger api docs from Rails
56
70
  controllers.
57
71
  email:
@@ -65,12 +79,26 @@ files:
65
79
  - ".travis.yml"
66
80
  - CODE_OF_CONDUCT.md
67
81
  - Gemfile
82
+ - Gemfile.lock
68
83
  - LICENSE.txt
69
84
  - README.md
70
85
  - Rakefile
71
86
  - bin/console
72
87
  - bin/setup
73
88
  - lib/swagger/api.rb
89
+ - lib/swagger/api/actions.rb
90
+ - lib/swagger/api/column_schema.rb
91
+ - lib/swagger/api/component_schema.rb
92
+ - lib/swagger/api/components.rb
93
+ - lib/swagger/api/concerns/columns.rb
94
+ - lib/swagger/api/operations/base.rb
95
+ - lib/swagger/api/operations/create.rb
96
+ - lib/swagger/api/operations/delete.rb
97
+ - lib/swagger/api/operations/index.rb
98
+ - lib/swagger/api/operations/show.rb
99
+ - lib/swagger/api/operations/update.rb
100
+ - lib/swagger/api/paths.rb
101
+ - lib/swagger/api/request_bodies.rb
74
102
  - lib/swagger/api/version.rb
75
103
  - swagger-api.gemspec
76
104
  homepage: https://gitlab.com/fullmeasure/public/swagger-api/swagger-api