dox 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "dox"
3
+ require 'bundler/setup'
4
+ require 'dox'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "dox"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start
data/dox.gemspec CHANGED
@@ -1,5 +1,4 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'dox/version'
5
4
 
@@ -9,31 +8,33 @@ Gem::Specification.new do |spec|
9
8
  spec.authors = ['Melita Kokot', 'Vedran Hrnčić']
10
9
  spec.email = ['melita.kokot@gmail.com', 'vrabac266@gmail.com']
11
10
 
12
- spec.summary = 'Generates API documentation for rspec in api blueprint format.'
11
+ spec.summary = 'Generates API documentation for rspec in OpenAPI format.'
13
12
  spec.homepage = 'https://github.com/infinum/dox'
14
13
  spec.license = 'MIT'
15
14
 
16
- spec.required_ruby_version = '>=2.0.0'
15
+ spec.required_ruby_version = '>= 2.0.0'
17
16
 
18
17
  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
19
18
  # delete this section to allow pushing this gem to any host.
20
- if spec.respond_to?(:metadata)
21
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
22
- else
23
- raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
24
- end
19
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
20
+
21
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
25
22
 
26
23
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
24
  spec.bindir = 'exe'
28
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
26
  spec.require_paths = ['lib']
30
27
 
31
- spec.add_development_dependency 'bundler', '~> 1.11'
32
- spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'bundler'
29
+ spec.add_development_dependency 'codeclimate-test-reporter'
30
+ spec.add_development_dependency 'json'
31
+ spec.add_development_dependency 'pry-nav'
32
+ spec.add_development_dependency 'pry-rails'
33
+ spec.add_development_dependency 'pry-stack_explorer'
34
+ spec.add_runtime_dependency 'rails', '>= 4.0'
35
+ spec.add_development_dependency 'rake'
33
36
  spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'pry'
37
+ spec.add_development_dependency 'rubocop'
35
38
  spec.add_development_dependency 'simplecov'
36
- spec.add_development_dependency 'codeclimate-test-reporter'
37
39
  spec.add_runtime_dependency 'rspec-core'
38
- spec.add_runtime_dependency 'rails', '>= 4.0'
39
40
  end
data/lib/dox.rb CHANGED
@@ -19,10 +19,16 @@ require 'dox/errors/invalid_action_error'
19
19
  require 'dox/errors/invalid_resource_error'
20
20
  require 'dox/errors/invalid_resource_group_error'
21
21
  require 'dox/formatter'
22
+ require 'dox/formatters/base'
23
+ require 'dox/formatters/json'
24
+ require 'dox/formatters/multipart'
25
+ require 'dox/formatters/plain'
26
+ require 'dox/formatters/xml'
22
27
  require 'dox/printers/base_printer'
23
28
  require 'dox/printers/action_printer'
24
29
  require 'dox/printers/document_printer'
25
- require 'dox/printers/example_printer'
30
+ require 'dox/printers/example_request_printer'
31
+ require 'dox/printers/example_response_printer'
26
32
  require 'dox/printers/resource_group_printer'
27
33
  require 'dox/printers/resource_printer'
28
34
  require 'dox/util/http'
@@ -43,4 +49,11 @@ module Dox
43
49
  def self.full_headers_whitelist
44
50
  (config.headers_whitelist.to_a + DEFAULT_HEADERS_WHITELIST).uniq
45
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
46
59
  end
data/lib/dox/config.rb CHANGED
@@ -1,16 +1,50 @@
1
1
  module Dox
2
2
  class Config
3
- attr_reader :header_file_path, :desc_folder_path
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 :header_description
11
+ attr_accessor :groups_order
12
+ attr_reader :descriptions_location
5
13
 
6
- def header_file_path=(file_path)
14
+ def descriptions_location=(folder_path)
15
+ raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
16
+
17
+ @descriptions_location = folder_path
18
+ end
19
+
20
+ def schema_request_folder_path=(folder_path)
21
+ raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
22
+
23
+ @schema_request_folder_path = folder_path
24
+ end
25
+
26
+ def schema_response_folder_path=(folder_path)
27
+ raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
28
+
29
+ @schema_response_folder_path = folder_path
30
+ end
31
+
32
+ def schema_response_fail_file_path=(file_path)
7
33
  raise(Errors::FileNotFoundError, file_path) unless File.exist?(file_path)
8
- @header_file_path = file_path
34
+
35
+ @schema_response_fail_file_path = file_path
9
36
  end
10
37
 
11
38
  def desc_folder_path=(folder_path)
12
- raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
13
- @desc_folder_path = folder_path
39
+ warn(
40
+ 'DEPRECATION WARNING: desc_folder_path will be removed in the next release, please use descriptions_location instead' # rubocop:disable Layout/LineLength
41
+ )
42
+
43
+ self.descriptions_location = folder_path
44
+ end
45
+
46
+ def header_file_path=(_file_path)
47
+ warn('WARNING: header_file_path is no longer used. Move header description to config.header_description.')
14
48
  end
15
49
  end
16
50
  end
@@ -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.presence
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 : {})
@@ -16,7 +16,6 @@ module Dox
16
16
 
17
17
  raise(Dox::Errors::InvalidResourceError, 'Resource name is required!') if @name.blank?
18
18
  raise(Dox::Errors::InvalidResourceError, 'Resource group is required!') if @group.blank?
19
- raise(Dox::Errors::InvalidResourceError, 'Resource endpoint is required!') if @endpoint.blank?
20
19
  end
21
20
 
22
21
  def config
@@ -24,7 +23,6 @@ module Dox
24
23
  resource_name: @name.presence,
25
24
  resource_desc: @desc.presence,
26
25
  resource_group_name: @group.presence,
27
- resource_endpoint: @endpoint.presence,
28
26
  apidoc: true
29
27
  }
30
28
  end
@@ -10,6 +10,7 @@ module Dox
10
10
 
11
11
  def const_missing(name)
12
12
  documentation = _subjects[infer_subject(name)]
13
+
13
14
  return super unless documentation
14
15
 
15
16
  Module.new do
@@ -1,16 +1,17 @@
1
1
  module Dox
2
2
  module Entities
3
3
  class Action
4
- attr_reader :name, :desc, :verb, :path, :uri_params
4
+ attr_reader :name, :desc, :verb, :path, :resource, :params
5
5
  attr_accessor :examples
6
6
 
7
- def initialize(name, details, request)
7
+ def initialize(details, request)
8
8
  @request = request
9
- @name = 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
- @uri_params = details[:action_params] || template_path_params
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("?").first
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
- @path_params ||=
34
- request.path_parameters.symbolize_keys.except(:action, :controller, :format, :subdomain)
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 template_path_params
38
- h = {}
39
- path_params.each do |param, value|
40
- param_type = guess_param_type(value)
41
- h[param] = { type: param_type, required: :required, value: value }
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)
@@ -3,20 +3,29 @@ 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
- def_delegator :response, :body, :response_body
9
10
  def_delegator :request, :content_type, :request_content_type
10
11
  def_delegator :request, :method, :request_method
11
12
 
12
13
  def initialize(details, request, response)
13
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]
14
19
  @request = request
15
20
  @response = response
16
21
  end
17
22
 
18
23
  def request_body
19
- @request_body ||= request.body.read
24
+ @request_body ||= format_content(request, request_content_type)
25
+ end
26
+
27
+ def response_body
28
+ @response_body ||= format_content(response, response_content_type)
20
29
  end
21
30
 
22
31
  def request_identifier
@@ -31,6 +40,10 @@ module Dox
31
40
  @request_headers ||= filter_headers(request)
32
41
  end
33
42
 
43
+ def response_success?
44
+ response.successful?
45
+ end
46
+
34
47
  # Rails 4 includes the body params in the request_fullpath
35
48
  def request_fullpath
36
49
  if request.query_parameters.present?
@@ -42,12 +55,29 @@ module Dox
42
55
 
43
56
  private
44
57
 
58
+ def format_content(http_env, content_type)
59
+ formatter(content_type).new(http_env).format
60
+ end
61
+
62
+ def formatter(content_type)
63
+ case content_type
64
+ when %r{application\/.*json}
65
+ Dox::Formatters::Json
66
+ when /xml/
67
+ Dox::Formatters::Xml
68
+ when /multipart/
69
+ Dox::Formatters::Multipart
70
+ else
71
+ Dox::Formatters::Plain
72
+ end
73
+ end
74
+
45
75
  # Rails 5.0.2 returns "" for request.path
46
76
  def request_path
47
77
  request.path.presence || request.fullpath.split('?')[0]
48
78
  end
49
79
 
50
- attr_reader :desc, :request, :response
80
+ attr_reader :request, :response
51
81
 
52
82
  def filter_headers(obj)
53
83
  headers_whitelist.map do |header|
@@ -1,17 +1,15 @@
1
1
  module Dox
2
2
  module Entities
3
3
  class Resource
4
-
5
- attr_reader :name, :desc, :endpoint
4
+ attr_reader :name, :desc, :group
6
5
  attr_accessor :actions
7
6
 
8
- def initialize(name, details)
9
- @name = name
7
+ def initialize(details)
8
+ @name = details[:resource_name]
9
+ @group = details[:resource_group_name]
10
10
  @desc = details[:resource_desc]
11
- @endpoint = details[:resource_endpoint]
12
11
  @actions = {}
13
12
  end
14
-
15
13
  end
16
14
  end
17
15
  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(name, details)
9
- @name = 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
- $stderr.puts(notification.fully_formatted_failed_examples)
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
- $stderr.puts(summary.fully_formatted)
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(group_name,
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 = current_example.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 = current_example.action_name
60
- resource.actions[action_name] ||= Entities::Action.new(action_name, current_example.metadata,
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, current_example.request, current_example.response)
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