dox 1.2.0 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +6 -3
- data/.ruby-version +1 -1
- data/.travis.yml +7 -11
- data/CHANGES.md +17 -0
- data/Gemfile +1 -1
- data/README.md +90 -108
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/dox.gemspec +14 -13
- data/lib/dox.rb +9 -1
- data/lib/dox/config.rb +38 -5
- data/lib/dox/dsl/action.rb +10 -4
- data/lib/dox/dsl/documentation.rb +4 -0
- data/lib/dox/dsl/syntax.rb +1 -0
- data/lib/dox/entities/action.rb +33 -13
- data/lib/dox/entities/example.rb +11 -1
- data/lib/dox/entities/resource.rb +4 -5
- data/lib/dox/entities/resource_group.rb +2 -3
- data/lib/dox/formatter.rb +10 -11
- data/lib/dox/printers/action_printer.rb +18 -27
- data/lib/dox/printers/base_printer.rb +36 -17
- data/lib/dox/printers/document_printer.rb +27 -7
- data/lib/dox/printers/example_request_printer.rb +69 -0
- data/lib/dox/printers/example_response_printer.rb +86 -0
- data/lib/dox/printers/resource_group_printer.rb +7 -7
- data/lib/dox/printers/resource_printer.rb +12 -7
- data/lib/dox/util/http.rb +64 -0
- data/lib/dox/version.rb +1 -1
- metadata +90 -34
- data/lib/dox/printers/example_printer.rb +0 -100
data/lib/dox.rb
CHANGED
@@ -27,7 +27,8 @@ require 'dox/formatters/xml'
|
|
27
27
|
require 'dox/printers/base_printer'
|
28
28
|
require 'dox/printers/action_printer'
|
29
29
|
require 'dox/printers/document_printer'
|
30
|
-
require 'dox/printers/
|
30
|
+
require 'dox/printers/example_request_printer'
|
31
|
+
require 'dox/printers/example_response_printer'
|
31
32
|
require 'dox/printers/resource_group_printer'
|
32
33
|
require 'dox/printers/resource_printer'
|
33
34
|
require 'dox/util/http'
|
@@ -48,4 +49,11 @@ module Dox
|
|
48
49
|
def self.full_headers_whitelist
|
49
50
|
(config.headers_whitelist.to_a + DEFAULT_HEADERS_WHITELIST).uniq
|
50
51
|
end
|
52
|
+
|
53
|
+
RSpec.configure do |config|
|
54
|
+
config.after(:each, :dox) do |example|
|
55
|
+
example.metadata[:request] = request
|
56
|
+
example.metadata[:response] = response
|
57
|
+
end
|
58
|
+
end
|
51
59
|
end
|
data/lib/dox/config.rb
CHANGED
@@ -1,16 +1,49 @@
|
|
1
1
|
module Dox
|
2
2
|
class Config
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :schema_request_folder_path
|
4
|
+
attr_reader :schema_response_folder_path
|
5
|
+
attr_reader :schema_response_fail_file_path
|
4
6
|
attr_accessor :headers_whitelist
|
7
|
+
attr_accessor :openapi_version
|
8
|
+
attr_accessor :api_version
|
9
|
+
attr_accessor :title
|
10
|
+
attr_accessor :description
|
11
|
+
attr_reader :descriptions_location
|
5
12
|
|
6
|
-
def
|
13
|
+
def descriptions_location=(folder_path)
|
14
|
+
raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
|
15
|
+
|
16
|
+
@descriptions_location = folder_path
|
17
|
+
end
|
18
|
+
|
19
|
+
def schema_request_folder_path=(folder_path)
|
20
|
+
raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
|
21
|
+
|
22
|
+
@schema_request_folder_path = folder_path
|
23
|
+
end
|
24
|
+
|
25
|
+
def schema_response_folder_path=(folder_path)
|
26
|
+
raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
|
27
|
+
|
28
|
+
@schema_response_folder_path = folder_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def schema_response_fail_file_path=(file_path)
|
7
32
|
raise(Errors::FileNotFoundError, file_path) unless File.exist?(file_path)
|
8
|
-
|
33
|
+
|
34
|
+
@schema_response_fail_file_path = file_path
|
9
35
|
end
|
10
36
|
|
11
37
|
def desc_folder_path=(folder_path)
|
12
|
-
|
13
|
-
|
38
|
+
warn(
|
39
|
+
'DEPRECATION WARNING: desc_folder_path will be removed in the next release, please use descriptions_location instead' # rubocop:disable Layout/LineLength
|
40
|
+
)
|
41
|
+
|
42
|
+
self.descriptions_location = folder_path
|
43
|
+
end
|
44
|
+
|
45
|
+
def header_file_path=(_file_path)
|
46
|
+
warn('WARNING: header_file_path is no longer used. Move header description to config.header_description.')
|
14
47
|
end
|
15
48
|
end
|
16
49
|
end
|
data/lib/dox/dsl/action.rb
CHANGED
@@ -8,6 +8,10 @@ module Dox
|
|
8
8
|
attr_writer :path
|
9
9
|
attr_writer :desc
|
10
10
|
attr_writer :params
|
11
|
+
attr_writer :query_params
|
12
|
+
attr_writer :request_schema
|
13
|
+
attr_writer :response_schema_success
|
14
|
+
attr_writer :response_schema_fail
|
11
15
|
|
12
16
|
def initialize(name, &block)
|
13
17
|
self.name = name
|
@@ -17,13 +21,15 @@ module Dox
|
|
17
21
|
end
|
18
22
|
|
19
23
|
def config
|
20
|
-
{
|
24
|
+
{ action_request_schema: @request_schema.presence,
|
25
|
+
action_response_schema_success: @response_schema_success.presence,
|
26
|
+
action_response_schema_fail: @response_schema_fail.presence,
|
21
27
|
action_name: @name.presence,
|
22
28
|
action_verb: @verb.presence,
|
23
29
|
action_path: @path.presence,
|
24
|
-
action_desc: @desc.presence,
|
25
|
-
action_params: @params
|
26
|
-
|
30
|
+
action_desc: @desc.presence || '',
|
31
|
+
action_params: @params,
|
32
|
+
action_query_params: @query_params.presence || [] }
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
@@ -14,6 +14,8 @@ module Dox
|
|
14
14
|
self._resource = Resource.new(name, &block)
|
15
15
|
end
|
16
16
|
|
17
|
+
alias tag resource
|
18
|
+
|
17
19
|
def action(name, &block)
|
18
20
|
self._action = Action.new(name, &block)
|
19
21
|
end
|
@@ -22,6 +24,8 @@ module Dox
|
|
22
24
|
self._group = ResourceGroup.new(name, &block)
|
23
25
|
end
|
24
26
|
|
27
|
+
alias x_tag group
|
28
|
+
|
25
29
|
def config
|
26
30
|
{}.merge(_resource ? _resource.config : {})
|
27
31
|
.merge(_action ? _action.config : {})
|
data/lib/dox/dsl/syntax.rb
CHANGED
data/lib/dox/entities/action.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
module Dox
|
2
2
|
module Entities
|
3
3
|
class Action
|
4
|
-
attr_reader :name, :desc, :verb, :path, :
|
4
|
+
attr_reader :name, :desc, :verb, :path, :resource, :params
|
5
5
|
attr_accessor :examples
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(details, request)
|
8
8
|
@request = request
|
9
|
-
@name =
|
9
|
+
@name = details[:action_name]
|
10
|
+
@resource = details[:resource_name]
|
10
11
|
@desc = details[:action_desc]
|
11
12
|
@verb = details[:action_verb] || request.method
|
12
13
|
@path = details[:action_path] || template_path
|
13
|
-
@
|
14
|
+
@params = template_params(details[:action_params], details[:action_query_params] || [])
|
14
15
|
@examples = []
|
15
16
|
|
16
17
|
validate!
|
@@ -22,25 +23,44 @@ module Dox
|
|
22
23
|
|
23
24
|
# /pokemons/1 => pokemons/{id}
|
24
25
|
def template_path
|
25
|
-
path = request.path.dup.presence || request.fullpath.split(
|
26
|
+
path = request.path.dup.presence || request.fullpath.split('?').first
|
26
27
|
path_params.each do |key, value|
|
27
28
|
path.sub!(%r{\/#{value}(\/|$)}, "/{#{key}}\\1")
|
28
29
|
end
|
30
|
+
|
29
31
|
path
|
30
32
|
end
|
31
33
|
|
34
|
+
def template_params(defined_params, query_params)
|
35
|
+
acquire_path_params + acquire_defined_params(defined_params) + query_params
|
36
|
+
end
|
37
|
+
|
32
38
|
def path_params
|
33
|
-
|
34
|
-
|
39
|
+
request.path_parameters.symbolize_keys.except(:action, :controller, :format, :subdomain)
|
40
|
+
end
|
41
|
+
|
42
|
+
def acquire_path_params
|
43
|
+
return [] if path_params.nil?
|
44
|
+
|
45
|
+
path_params.map do |param, value|
|
46
|
+
{ name: param,
|
47
|
+
in: :path,
|
48
|
+
schema: { type: guess_param_type(value) },
|
49
|
+
example: value }
|
50
|
+
end
|
35
51
|
end
|
36
52
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
53
|
+
def acquire_defined_params(defined_params)
|
54
|
+
return [] if defined_params.nil?
|
55
|
+
|
56
|
+
defined_params.map do |key, value|
|
57
|
+
{ name: key,
|
58
|
+
in: 'query',
|
59
|
+
required: value[:required],
|
60
|
+
example: value[:value],
|
61
|
+
description: value[:description].presence || '',
|
62
|
+
schema: { type: value[:type] } }
|
42
63
|
end
|
43
|
-
h
|
44
64
|
end
|
45
65
|
|
46
66
|
def guess_param_type(param)
|
data/lib/dox/entities/example.rb
CHANGED
@@ -3,6 +3,8 @@ module Dox
|
|
3
3
|
class Example
|
4
4
|
extend Forwardable
|
5
5
|
|
6
|
+
attr_reader :desc, :name, :request_schema, :response_schema_success, :response_schema_fail
|
7
|
+
|
6
8
|
def_delegator :response, :status, :response_status
|
7
9
|
def_delegator :response, :content_type, :response_content_type
|
8
10
|
def_delegator :request, :content_type, :request_content_type
|
@@ -10,6 +12,10 @@ module Dox
|
|
10
12
|
|
11
13
|
def initialize(details, request, response)
|
12
14
|
@desc = details[:description]
|
15
|
+
@name = details[:resource_name].downcase
|
16
|
+
@request_schema = details[:action_request_schema]
|
17
|
+
@response_schema_success = details[:action_response_schema_success]
|
18
|
+
@response_schema_fail = details[:action_response_schema_fail]
|
13
19
|
@request = request
|
14
20
|
@response = response
|
15
21
|
end
|
@@ -34,6 +40,10 @@ module Dox
|
|
34
40
|
@request_headers ||= filter_headers(request)
|
35
41
|
end
|
36
42
|
|
43
|
+
def response_success?
|
44
|
+
response.successful?
|
45
|
+
end
|
46
|
+
|
37
47
|
# Rails 4 includes the body params in the request_fullpath
|
38
48
|
def request_fullpath
|
39
49
|
if request.query_parameters.present?
|
@@ -67,7 +77,7 @@ module Dox
|
|
67
77
|
request.path.presence || request.fullpath.split('?')[0]
|
68
78
|
end
|
69
79
|
|
70
|
-
attr_reader :
|
80
|
+
attr_reader :request, :response
|
71
81
|
|
72
82
|
def filter_headers(obj)
|
73
83
|
headers_whitelist.map do |header|
|
@@ -1,17 +1,16 @@
|
|
1
1
|
module Dox
|
2
2
|
module Entities
|
3
3
|
class Resource
|
4
|
-
|
5
|
-
attr_reader :name, :desc, :endpoint
|
4
|
+
attr_reader :name, :desc, :endpoint, :group
|
6
5
|
attr_accessor :actions
|
7
6
|
|
8
|
-
def initialize(
|
9
|
-
@name =
|
7
|
+
def initialize(details)
|
8
|
+
@name = details[:resource_name]
|
9
|
+
@group = details[:resource_group_name]
|
10
10
|
@desc = details[:resource_desc]
|
11
11
|
@endpoint = details[:resource_endpoint]
|
12
12
|
@actions = {}
|
13
13
|
end
|
14
|
-
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module Dox
|
2
2
|
module Entities
|
3
3
|
class ResourceGroup
|
4
|
-
|
5
4
|
attr_reader :name
|
6
5
|
attr_accessor :desc, :resources
|
7
6
|
|
8
|
-
def initialize(
|
9
|
-
@name =
|
7
|
+
def initialize(details)
|
8
|
+
@name = details[:resource_group_name]
|
10
9
|
@desc = details[:resource_group_desc]
|
11
10
|
@resources = {}
|
12
11
|
end
|
data/lib/dox/formatter.rb
CHANGED
@@ -20,7 +20,7 @@ module Dox
|
|
20
20
|
|
21
21
|
def stop(notification)
|
22
22
|
if notification.failed_examples.any?
|
23
|
-
|
23
|
+
warn(notification.fully_formatted_failed_examples)
|
24
24
|
else
|
25
25
|
printer.print(passed_examples)
|
26
26
|
end
|
@@ -28,7 +28,8 @@ module Dox
|
|
28
28
|
|
29
29
|
def dump_summary(summary)
|
30
30
|
return if summary.failed_examples.none?
|
31
|
-
|
31
|
+
|
32
|
+
warn(summary.fully_formatted)
|
32
33
|
exit(-1)
|
33
34
|
end
|
34
35
|
|
@@ -44,28 +45,26 @@ module Dox
|
|
44
45
|
if group
|
45
46
|
group.tap { |g| g.desc ||= current_example.resource_group_desc }
|
46
47
|
else
|
47
|
-
passed_examples[group_name] = Entities::ResourceGroup.new(
|
48
|
-
current_example.metadata)
|
48
|
+
passed_examples[group_name] = Entities::ResourceGroup.new(current_example.metadata)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
def load_or_save_resource_to_group(group)
|
53
|
-
resource_name
|
54
|
-
group.resources[resource_name] ||= Entities::Resource.new(resource_name,
|
55
|
-
current_example.metadata)
|
53
|
+
group.resources[current_example.resource_name] ||= Entities::Resource.new(current_example.metadata)
|
56
54
|
end
|
57
55
|
|
58
56
|
def load_or_save_action_to_resource(resource)
|
59
|
-
action_name
|
60
|
-
|
61
|
-
current_example.request)
|
57
|
+
resource.actions[current_example.action_name] ||= Entities::Action.new(current_example.metadata,
|
58
|
+
current_example.request)
|
62
59
|
end
|
63
60
|
|
64
61
|
def move_example_to_passed
|
65
62
|
group = load_or_save_group
|
66
63
|
resource = load_or_save_resource_to_group(group)
|
67
64
|
action = load_or_save_action_to_resource(resource)
|
68
|
-
action.examples << Entities::Example.new(current_example.metadata,
|
65
|
+
action.examples << Entities::Example.new(current_example.metadata,
|
66
|
+
current_example.request,
|
67
|
+
current_example.response)
|
69
68
|
end
|
70
69
|
|
71
70
|
def printer
|
@@ -3,44 +3,35 @@ module Dox
|
|
3
3
|
class ActionPrinter < BasePrinter
|
4
4
|
def print(action)
|
5
5
|
self.action = action
|
6
|
-
@
|
7
|
-
@output.puts action_uri_params if action.uri_params.present?
|
6
|
+
@action_hash = find_or_add(find_or_add(spec, action.path.to_s), action.verb.downcase.to_sym)
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
add_action
|
9
|
+
add_action_params
|
10
|
+
|
11
|
+
print_examples
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
attr_accessor :action
|
17
|
-
|
18
|
-
def action_title
|
19
|
-
<<-HEREDOC
|
16
|
+
attr_accessor :action, :action_hash
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
def add_action
|
19
|
+
action_hash['summary'] = action.name
|
20
|
+
action_hash['tags'] = [action.resource]
|
21
|
+
action_hash['description'] = format_desc(action.desc)
|
24
22
|
end
|
25
23
|
|
26
|
-
def
|
27
|
-
|
28
|
-
+ Parameters
|
29
|
-
#{formatted_params(action.uri_params)}
|
30
|
-
HEREDOC
|
31
|
-
end
|
24
|
+
def add_action_params
|
25
|
+
return unless action.params.present?
|
32
26
|
|
33
|
-
|
34
|
-
@example_printer ||= ExamplePrinter.new(@output)
|
27
|
+
action_hash['parameters'] = action.params
|
35
28
|
end
|
36
29
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
desc
|
43
|
-
end.flatten.join("\n")
|
30
|
+
def print_examples
|
31
|
+
action.examples.each do |example|
|
32
|
+
ExampleRequestPrinter.new(action_hash).print(example)
|
33
|
+
ExampleResponsePrinter.new(action_hash).print(example)
|
34
|
+
end
|
44
35
|
end
|
45
36
|
end
|
46
37
|
end
|
@@ -1,37 +1,56 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
1
3
|
module Dox
|
2
4
|
module Printers
|
3
5
|
class BasePrinter
|
4
|
-
|
5
|
-
|
6
|
+
attr_reader :spec
|
7
|
+
|
8
|
+
def initialize(spec)
|
9
|
+
@spec = spec || {}
|
6
10
|
end
|
7
11
|
|
8
12
|
def print
|
9
13
|
raise NotImplementedError
|
10
14
|
end
|
11
15
|
|
12
|
-
|
16
|
+
def find_or_add(hash, key, default = {})
|
17
|
+
return hash[key] if hash.key?(key)
|
13
18
|
|
14
|
-
|
15
|
-
Dox.config.desc_folder_path
|
19
|
+
hash[key] = default
|
16
20
|
end
|
17
21
|
|
18
|
-
def
|
19
|
-
return
|
22
|
+
def read_file(path, root_path: Dox.config.descriptions_location)
|
23
|
+
return '' unless root_path
|
24
|
+
|
25
|
+
File.read(File.join(root_path, path))
|
26
|
+
end
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
content(path)
|
28
|
+
def formatted_body(body_str, content_type)
|
29
|
+
case content_type
|
30
|
+
when %r{application\/.*json}
|
31
|
+
JSON.parse(body_str)
|
32
|
+
when /xml/
|
33
|
+
pretty_xml(body_str)
|
28
34
|
else
|
29
|
-
|
35
|
+
body_str
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
|
-
def
|
34
|
-
|
39
|
+
def pretty_xml(xml_string)
|
40
|
+
doc = REXML::Document.new(xml_string)
|
41
|
+
formatter = REXML::Formatters::Pretty.new
|
42
|
+
formatter.compact = true
|
43
|
+
result = ''
|
44
|
+
formatter.write(doc, result)
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def format_desc(description)
|
49
|
+
desc = description
|
50
|
+
desc = '' if desc.nil?
|
51
|
+
desc = read_file(desc) if desc.end_with?('.md')
|
52
|
+
|
53
|
+
desc
|
35
54
|
end
|
36
55
|
end
|
37
56
|
end
|