rspec_api_documentation 5.1.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
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