rspec_api_documentation 5.1.0 → 6.0.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rspec_api_documentation.rb +26 -1
  3. data/lib/rspec_api_documentation/api_documentation.rb +2 -0
  4. data/lib/rspec_api_documentation/configuration.rb +11 -1
  5. data/lib/rspec_api_documentation/dsl/endpoint.rb +33 -1
  6. data/lib/rspec_api_documentation/dsl/endpoint/params.rb +19 -3
  7. data/lib/rspec_api_documentation/dsl/endpoint/set_param.rb +12 -1
  8. data/lib/rspec_api_documentation/dsl/resource.rb +23 -0
  9. data/lib/rspec_api_documentation/open_api/contact.rb +9 -0
  10. data/lib/rspec_api_documentation/open_api/example.rb +7 -0
  11. data/lib/rspec_api_documentation/open_api/header.rb +12 -0
  12. data/lib/rspec_api_documentation/open_api/headers.rb +7 -0
  13. data/lib/rspec_api_documentation/open_api/helper.rb +29 -0
  14. data/lib/rspec_api_documentation/open_api/info.rb +12 -0
  15. data/lib/rspec_api_documentation/open_api/license.rb +8 -0
  16. data/lib/rspec_api_documentation/open_api/node.rb +112 -0
  17. data/lib/rspec_api_documentation/open_api/operation.rb +18 -0
  18. data/lib/rspec_api_documentation/open_api/parameter.rb +33 -0
  19. data/lib/rspec_api_documentation/open_api/path.rb +13 -0
  20. data/lib/rspec_api_documentation/open_api/paths.rb +7 -0
  21. data/lib/rspec_api_documentation/open_api/response.rb +10 -0
  22. data/lib/rspec_api_documentation/open_api/responses.rb +9 -0
  23. data/lib/rspec_api_documentation/open_api/root.rb +21 -0
  24. data/lib/rspec_api_documentation/open_api/schema.rb +15 -0
  25. data/lib/rspec_api_documentation/open_api/security_definitions.rb +7 -0
  26. data/lib/rspec_api_documentation/open_api/security_schema.rb +14 -0
  27. data/lib/rspec_api_documentation/open_api/tag.rb +9 -0
  28. data/lib/rspec_api_documentation/views/api_blueprint_example.rb +15 -3
  29. data/lib/rspec_api_documentation/views/api_blueprint_index.rb +17 -2
  30. data/lib/rspec_api_documentation/views/markdown_example.rb +1 -1
  31. data/lib/rspec_api_documentation/views/markup_example.rb +8 -3
  32. data/lib/rspec_api_documentation/views/slate_index.rb +4 -0
  33. data/lib/rspec_api_documentation/writers/combined_json_writer.rb +1 -1
  34. data/lib/rspec_api_documentation/writers/json_iodocs_writer.rb +2 -2
  35. data/lib/rspec_api_documentation/writers/json_writer.rb +6 -6
  36. data/lib/rspec_api_documentation/writers/markdown_writer.rb +1 -1
  37. data/lib/rspec_api_documentation/writers/open_api_writer.rb +244 -0
  38. data/lib/rspec_api_documentation/writers/slate_writer.rb +2 -8
  39. data/templates/rspec_api_documentation/api_blueprint_index.mustache +6 -5
  40. data/templates/rspec_api_documentation/slate_index.mustache +8 -0
  41. metadata +57 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5b64de927068173bdb8b5090658a90cfdfdebd87
4
- data.tar.gz: 7d671588a963248bb37ca1b86d2ccb6139c5e4d0
3
+ metadata.gz: debdb41655fc2b0d61d43a0feaffa6a3c4e1ae6c
4
+ data.tar.gz: e560cee22b88bda2b3779a4e9f26561e3d4195ba
5
5
  SHA512:
6
- metadata.gz: 45149d40631eb2c476fa099fb728da825ef831adbbe2032ae2ca07aa4b659e0950b6ac83c59568f83845c6328266a4a697ac8165308a385b2955c3a655143889
7
- data.tar.gz: 06d59a0dd3153544799cdaa3803894f2e0210d97ff72e17812194a0b565ee5bc1b369e23ae6a66f93a8b03f54d0224024aa2adfa72d3ff774a084f5589ce795b
6
+ metadata.gz: 6530d774c6e77fe56a65ee073d74f7b7acec77d8feeb63aace0b0b931975f59253be12d8c091c6bae743b884db9cadf723159f59c273c0dfe2acfdb2eb0106a3
7
+ data.tar.gz: 08e6146400b65497daea2d47c257ecead4436a37779b689215bab75830aab20f66e9e71698cb14a02101aea334555aae8be69aad56736e69ba483581b150aea3
@@ -37,7 +37,7 @@ module RspecApiDocumentation
37
37
  autoload :HtmlWriter
38
38
  autoload :TextileWriter
39
39
  autoload :MarkdownWriter
40
- autoload :JsonWriter
40
+ autoload :JSONWriter
41
41
  autoload :AppendJsonWriter
42
42
  autoload :JsonIodocsWriter
43
43
  autoload :IndexHelper
@@ -45,6 +45,31 @@ module RspecApiDocumentation
45
45
  autoload :CombinedJsonWriter
46
46
  autoload :SlateWriter
47
47
  autoload :ApiBlueprintWriter
48
+ autoload :OpenApiWriter
49
+ end
50
+
51
+ module OpenApi
52
+ extend ActiveSupport::Autoload
53
+
54
+ autoload :Helper
55
+ autoload :Node
56
+ autoload :Root
57
+ autoload :Info
58
+ autoload :Contact
59
+ autoload :License
60
+ autoload :Paths
61
+ autoload :Path
62
+ autoload :Tag
63
+ autoload :Operation
64
+ autoload :Parameter
65
+ autoload :Responses
66
+ autoload :Response
67
+ autoload :Example
68
+ autoload :Headers
69
+ autoload :Header
70
+ autoload :Schema
71
+ autoload :SecurityDefinitions
72
+ autoload :SecuritySchema
48
73
  end
49
74
 
50
75
  module Views
@@ -1,3 +1,5 @@
1
+ require 'rspec_api_documentation/writers/json_iodocs_writer'
2
+
1
3
  module RspecApiDocumentation
2
4
  class ApiDocumentation
3
5
  attr_reader :configuration, :index
@@ -51,6 +51,14 @@ module RspecApiDocumentation
51
51
  end
52
52
  end
53
53
 
54
+ add_setting :configurations_dir, :default => lambda { |config|
55
+ if defined?(Rails)
56
+ Rails.root.join('doc', 'configurations', 'api')
57
+ else
58
+ Pathname.new('doc/configurations/api')
59
+ end
60
+ }
61
+
54
62
  add_setting :docs_dir, :default => lambda { |config|
55
63
  if defined?(Rails)
56
64
  Rails.root.join("doc", "api")
@@ -110,7 +118,7 @@ module RspecApiDocumentation
110
118
  # See RspecApiDocumentation::DSL::Endpoint#do_request
111
119
  add_setting :response_body_formatter, default: Proc.new { |_, _|
112
120
  Proc.new do |content_type, response_body|
113
- if content_type =~ /application\/json/
121
+ if content_type =~ /application\/.*json/
114
122
  JSON.pretty_generate(JSON.parse(response_body))
115
123
  else
116
124
  response_body
@@ -119,6 +127,8 @@ module RspecApiDocumentation
119
127
  }
120
128
 
121
129
  def client_method=(new_client_method)
130
+ return if new_client_method == client_method
131
+
122
132
  RspecApiDocumentation::DSL::Resource.module_eval <<-RUBY
123
133
  alias :#{new_client_method} #{client_method}
124
134
  undef #{client_method}
@@ -38,6 +38,9 @@ module RspecApiDocumentation::DSL
38
38
  params_or_body = nil
39
39
  path_or_query = path
40
40
 
41
+ extended_parameters
42
+ extract_route_parameters!
43
+
41
44
  if http_method == :get && !query_string.blank?
42
45
  path_or_query += "?#{query_string}"
43
46
  else
@@ -74,6 +77,36 @@ module RspecApiDocumentation::DSL
74
77
  example.metadata[:headers][name] = value
75
78
  end
76
79
 
80
+ def authentication(type, value, opts = {})
81
+ name, new_opts =
82
+ case type
83
+ when :basic then ['Authorization', opts.merge(type: type)]
84
+ when :apiKey then [opts[:name], opts.merge(type: type, in: :header)]
85
+ else raise 'Not supported type for authentication'
86
+ end
87
+ header(name, value)
88
+ example.metadata[:authentications] ||= {}
89
+ example.metadata[:authentications][name] = new_opts
90
+ end
91
+
92
+ def extract_route_parameters!
93
+ example.metadata[:route].gsub(URL_PARAMS_REGEX) do |match|
94
+ value =
95
+ if extra_params.keys.include?($1)
96
+ extra_params[$1]
97
+ elsif respond_to?($1)
98
+ send($1)
99
+ else
100
+ match
101
+ end
102
+ extended_parameters << {name: match[1..-1], value: value, in: :path}
103
+ end
104
+ end
105
+
106
+ def extended_parameters
107
+ example.metadata[:extended_parameters] ||= Params.new(self, example, extra_params).extended
108
+ end
109
+
77
110
  def headers
78
111
  return unless example.metadata[:headers]
79
112
  example.metadata[:headers].inject({}) do |hash, (header, value)|
@@ -144,6 +177,5 @@ module RspecApiDocumentation::DSL
144
177
  def delete_extra_param(key)
145
178
  @extra_params.delete(key.to_sym) || @extra_params.delete(key.to_s)
146
179
  end
147
-
148
180
  end
149
181
  end
@@ -13,11 +13,27 @@ module RspecApiDocumentation
13
13
  end
14
14
 
15
15
  def call
16
- parameters = example.metadata.fetch(:parameters, {}).inject({}) do |hash, param|
16
+ set_param = -> hash, param {
17
17
  SetParam.new(self, hash, param).call
18
+ }
19
+
20
+ example.metadata.fetch(:parameters, {}).inject({}, &set_param)
21
+ .deep_merge(
22
+ example.metadata.fetch(:attributes, {}).inject({}, &set_param)
23
+ ).deep_merge(extra_params)
24
+ end
25
+
26
+ def extended
27
+ example.metadata.fetch(:parameters, {}).map do |param|
28
+ p = Marshal.load(Marshal.dump(param))
29
+ p[:value] = SetParam.new(self, nil, p).value
30
+ unless p[:value]
31
+ cur = extra_params
32
+ [*p[:scope]].each { |scope| cur = cur && (cur[scope.to_sym] || cur[scope.to_s]) }
33
+ p[:value] = cur && (cur[p[:name].to_s] || cur[p[:name].to_sym])
34
+ end
35
+ p
18
36
  end
19
- parameters.deep_merge!(extra_params)
20
- parameters
21
37
  end
22
38
 
23
39
  private
@@ -15,6 +15,10 @@ module RspecApiDocumentation
15
15
  hash.deep_merge build_param_hash(key_scope || [key])
16
16
  end
17
17
 
18
+ def value
19
+ example_group.send(method_name) if method_name
20
+ end
21
+
18
22
  private
19
23
 
20
24
  attr_reader :parent, :hash, :param
@@ -36,6 +40,10 @@ module RspecApiDocumentation
36
40
  param[:method]
37
41
  end
38
42
 
43
+ def set_value
44
+ param[:value]
45
+ end
46
+
39
47
  def path_name
40
48
  scoped_key || key
41
49
  end
@@ -51,11 +59,14 @@ module RspecApiDocumentation
51
59
  scoped_key
52
60
  elsif key && example_group.respond_to?(key)
53
61
  key
62
+ elsif key && set_value
63
+ key
54
64
  end
55
65
  end
56
66
 
57
67
  def build_param_hash(keys)
58
- value = keys[1] ? build_param_hash(keys[1..-1]) : example_group.send(method_name)
68
+ value = param[:value] if param.has_key?(:value)
69
+ value ||= keys[1] ? build_param_hash(keys[1..-1]) : example_group.send(method_name)
59
70
  { keys[0].to_s => value }
60
71
  end
61
72
  end
@@ -70,6 +70,25 @@ module RspecApiDocumentation::DSL
70
70
  headers[name] = value
71
71
  end
72
72
 
73
+ def authentication(type, value, opts = {})
74
+ name, new_opts =
75
+ case type
76
+ when :basic then ['Authorization', opts.merge(type: type)]
77
+ when :apiKey then [opts[:name], opts.merge(type: type, in: :header)]
78
+ else raise 'Not supported type for authentication'
79
+ end
80
+ header(name, value)
81
+ authentications[name] = new_opts
82
+ end
83
+
84
+ def route_summary(text)
85
+ safe_metadata(:route_summary, text)
86
+ end
87
+
88
+ def route_description(text)
89
+ safe_metadata(:route_description, text)
90
+ end
91
+
73
92
  def explanation(text)
74
93
  safe_metadata(:resource_explanation, text)
75
94
  end
@@ -107,6 +126,10 @@ module RspecApiDocumentation::DSL
107
126
  safe_metadata(:headers, {})
108
127
  end
109
128
 
129
+ def authentications
130
+ safe_metadata(:authentications, {})
131
+ end
132
+
110
133
  def parameter_keys
111
134
  parameters.map { |param| param[:name] }
112
135
  end
@@ -0,0 +1,9 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ class Contact < Node
4
+ add_setting :name, :default => 'API Support'
5
+ add_setting :url, :default => 'http://www.open-api.io/support'
6
+ add_setting :email, :default => 'support@open-api.io'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ class Example < Node
4
+ CHILD_CLASS = true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ class Header < Node
4
+ add_setting :description, :default => ''
5
+ add_setting :type, :required => true, :default => lambda { |header|
6
+ Helper.extract_type(header.public_send('x-example-value'))
7
+ }
8
+ add_setting :format
9
+ add_setting 'x-example-value'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ class Headers < Node
4
+ CHILD_CLASS = Header
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ module Helper
4
+ module_function
5
+
6
+ def extract_type(value)
7
+ case value
8
+ when Rack::Test::UploadedFile then :file
9
+ when Array then :array
10
+ when Hash then :object
11
+ when TrueClass, FalseClass then :boolean
12
+ when Integer then :integer
13
+ when Float then :number
14
+ else :string
15
+ end
16
+ end
17
+
18
+ def extract_items(value, opts = {})
19
+ result = {type: extract_type(value)}
20
+ if result[:type] == :array
21
+ result[:items] = extract_items(value[0], opts)
22
+ else
23
+ opts.each { |k, v| result[k] = v if v }
24
+ end
25
+ result
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ class Info < Node
4
+ add_setting :title, :default => 'OpenAPI Specification', :required => true
5
+ add_setting :description, :default => 'This is a sample server Petstore server.'
6
+ add_setting :termsOfService, :default => 'http://open-api.io/terms/'
7
+ add_setting :contact, :default => Contact.new, :schema => Contact
8
+ add_setting :license, :default => License.new, :schema => License
9
+ add_setting :version, :default => '1.0.0', :required => true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ class License < Node
4
+ add_setting :name, :default => 'Apache 2.0', :required => true
5
+ add_setting :url, :default => 'http://www.apache.org/licenses/LICENSE-2.0.html'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,112 @@
1
+ module RspecApiDocumentation
2
+ module OpenApi
3
+ class Node
4
+ # this is used to define class of incoming option attribute
5
+ # If +false+ then do not create new setting
6
+ # If +true+ then create new setting with raw passed value
7
+ # If RspecApiDocumentation::OpenApi::Node then create new setting and wrap it in this class
8
+ CHILD_CLASS = false
9
+
10
+ # This attribute allow us to hide some of children through configuration file
11
+ attr_accessor :hide
12
+
13
+ def self.add_setting(name, opts = {})
14
+ class_settings << name
15
+
16
+ define_method("#{name}_schema") { opts[:schema] || NilClass }
17
+ define_method("#{name}=") { |value| settings[name] = value }
18
+ define_method("#{name}") do
19
+ if settings.has_key?(name)
20
+ settings[name]
21
+ elsif !opts[:default].nil?
22
+ if opts[:default].respond_to?(:call)
23
+ opts[:default].call(self)
24
+ else
25
+ opts[:default]
26
+ end
27
+ elsif opts[:required]
28
+ raise "setting: #{name} required in #{self}"
29
+ end
30
+ end
31
+ end
32
+
33
+ def initialize(opts = {})
34
+ return unless opts
35
+
36
+ opts.each do |name, value|
37
+ if name.to_s == 'hide'
38
+ self.hide = value
39
+ elsif self.class::CHILD_CLASS
40
+ add_setting name, :value => self.class::CHILD_CLASS === true ? value : self.class::CHILD_CLASS.new(value)
41
+ elsif setting_exist?(name.to_sym)
42
+ schema = setting_schema(name)
43
+ converted =
44
+ case
45
+ when schema.is_a?(Array) && schema[0] <= Node then value.map { |v| v.is_a?(schema[0]) ? v : schema[0].new(v) }
46
+ when schema <= Node then value.is_a?(schema) ? value : schema.new(value)
47
+ else
48
+ value
49
+ end
50
+ assign_setting(name, converted)
51
+ else
52
+ public_send("#{name}=", value) if respond_to?("#{name}=")
53
+ end
54
+ end
55
+ end
56
+
57
+ def assign_setting(name, value); public_send("#{name}=", value) unless value.nil? end
58
+ def safe_assign_setting(name, value); assign_setting(name, value) unless settings.has_key?(name) end
59
+ def setting(name); public_send(name) end
60
+ def setting_schema(name); public_send("#{name}_schema") end
61
+ def setting_exist?(name); existing_settings.include?(name) end
62
+ def existing_settings; self.class.class_settings + instance_settings end
63
+
64
+ def add_setting(name, opts = {})
65
+ return false if setting_exist?(name)
66
+
67
+ instance_settings << name
68
+
69
+ settings[name] = opts[:value] if opts[:value]
70
+
71
+ define_singleton_method("#{name}_schema") { opts[:schema] || NilClass }
72
+ define_singleton_method("#{name}=") { |value| settings[name] = value }
73
+ define_singleton_method("#{name}") do
74
+ if settings.has_key?(name)
75
+ settings[name]
76
+ elsif !opts[:default].nil?
77
+ if opts[:default].respond_to?(:call)
78
+ opts[:default].call(self)
79
+ else
80
+ opts[:default]
81
+ end
82
+ elsif opts[:required]
83
+ raise "setting: #{name} required in #{self}"
84
+ end
85
+ end
86
+ end
87
+
88
+ def as_json
89
+ existing_settings.inject({}) do |hash, name|
90
+ value = setting(name)
91
+ case
92
+ when value.is_a?(Node)
93
+ hash[name] = value.as_json unless value.hide
94
+ when value.is_a?(Array) && value[0].is_a?(Node)
95
+ tmp = value.select { |v| !v.hide }.map { |v| v.as_json }
96
+ hash[name] = tmp unless tmp.empty?
97
+ else
98
+ hash[name] = value
99
+ end unless value.nil?
100
+
101
+ hash
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def settings; @settings ||= {} end
108
+ def instance_settings; @instance_settings ||= [] end
109
+ def self.class_settings; @class_settings ||= [] end
110
+ end
111
+ end
112
+ end