fitting 2.0.3 → 2.1.0

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
  SHA1:
3
- metadata.gz: adba51bf994a83f976b645f80537581c51da7fd2
4
- data.tar.gz: 56df90a70b7dc86516d58706b945f35556e0c771
3
+ metadata.gz: a085daed12d7e3b16bdeb141928c50db861ff4ba
4
+ data.tar.gz: 73cf6002cb3b3b55a83c6c723a329f90c7a4f164
5
5
  SHA512:
6
- metadata.gz: 4beb00e43f17d6cd07a9eb0e14a62e7c384773f9285e12f44cd5dfa6c7342cfbfe2fcba1af8deab170b33d2837c6356710fe233b22ec5cb42d665537878d8f45
7
- data.tar.gz: 2be9f6e2ffb542e0b0391266ad8281ed435dd805027358b443ea875dfd807f1fc971fe28550fb4607740f9d9b887d282516ced737dd4735b64dbec2d61613d55
6
+ metadata.gz: 66b61927387c25a314c800dc5d385f34ed0a89c5bf754532674e64f092a3100e9476d539be357883b1e71b1094715fda3245bf4b6a4d6a0828380f5ba73e0b5a
7
+ data.tar.gz: d5ad8dd2673d588d9f0d2b32b0a2cac492a62787d6ddc600f14731d8fc24096ca8fa42376ce23e1ff4a0f4777af9b8f0d7d929bbc9927b4fb1dcdae929bced97
@@ -9,3 +9,12 @@ Style/Documentation:
9
9
 
10
10
  Style/FrozenStringLiteralComment:
11
11
  Enabled: false
12
+
13
+ Metrics/BlockLength:
14
+ Enabled: false
15
+
16
+ Style/CaseEquality:
17
+ Enabled: false
18
+
19
+ Style/NilComparison:
20
+ Enabled: false
@@ -3,9 +3,14 @@
3
3
  ### 2.0.2 - 2017-05-02
4
4
 
5
5
  * bug fixes
6
- * Validate html response
6
+ * validate html response
7
7
 
8
8
  ### 2.0.3 - 2017-05-02
9
9
 
10
10
  * bug fixes
11
- * Skip expect if prefix not match
11
+ * skip expect if prefix not match
12
+
13
+ ### 2.1.0 - 2017-06-19
14
+
15
+ * features
16
+ * add resource_white_list config
data/README.md CHANGED
@@ -131,7 +131,7 @@ Prefix of API requests. Example: `'/api'`. Validation will not be performed if t
131
131
 
132
132
  ### white_list
133
133
 
134
- Default: all resources. This is an array of resources that are mandatory for implementation.
134
+ Default: all paths. This is an array of paths that are mandatory for implementation.
135
135
  This list does not affect the work of the matcher.
136
136
  This list is only for the report in the console.
137
137
 
@@ -146,6 +146,22 @@ config.white_list = {
146
146
 
147
147
  Empty array `[]` means all methods.
148
148
 
149
+ ### resource_white_list
150
+
151
+ Default: all resources. This is an array of resources that are mandatory for implementation.
152
+ This list does not affect the work of the matcher.
153
+ This list is only for the report in the console.
154
+
155
+ ```ruby
156
+ config.resource_white_list = {
157
+ '/users' => ['DELETE /users/{id}', 'POST /users', 'GET /users/{id}', 'PATCH /users/{id}'],
158
+ '/users/{id}/employees' => ['GET /users/{id}/employees'],
159
+ '/sessions' => []
160
+ }
161
+ ```
162
+
163
+ Empty array `[]` means all methods.
164
+
149
165
  ## Contributing
150
166
 
151
167
  Bug reports and pull requests are welcome on GitHub at https://github.com/funbox/fitting. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_runtime_dependency 'json-schema', '~> 2.6', '>= 2.6.2'
23
23
  spec.add_runtime_dependency 'multi_json'
24
- spec.add_runtime_dependency 'tomograph', '~> 1.0', '>= 1.0.0'
24
+ spec.add_runtime_dependency 'tomograph', '~> 1.1', '>= 1.1.0'
25
25
  spec.add_development_dependency 'bundler', '~> 1.12'
26
26
  spec.add_development_dependency 'rake', '~> 10.0'
27
27
  spec.add_development_dependency 'byebug', '~> 8.2', '>= 8.2.1'
@@ -4,7 +4,8 @@ module Fitting
4
4
  :drafter_yaml_path,
5
5
  :strict,
6
6
  :prefix,
7
- :white_list
7
+ :white_list,
8
+ :resource_white_list
8
9
 
9
10
  def initialize
10
11
  @strict = false
@@ -10,8 +10,7 @@ module Fitting
10
10
  def black
11
11
  if @white_list
12
12
  all.select do |response|
13
- data = response.split(' ')
14
- data[1] && !@white_list[data[1]] || (@white_list[data[1]] != [] && !@white_list[data[1]].include?(data[0]))
13
+ black?(response)
15
14
  end
16
15
  else
17
16
  []
@@ -21,8 +20,7 @@ module Fitting
21
20
  def white
22
21
  if @white_list
23
22
  all.select do |response|
24
- data = response.split(' ')
25
- data[1] && @white_list[data[1]] && (@white_list[data[1]] == [] || @white_list[data[1]].include?(data[0]))
23
+ white?(response)
26
24
  end
27
25
  else
28
26
  all
@@ -30,19 +28,31 @@ module Fitting
30
28
  end
31
29
 
32
30
  def all
33
- @all ||= @tomogram.to_hash.inject([]) do |routes, request|
34
- request['responses'].inject({}) do |responses, response|
35
- responses[response['status']] ||= 0
36
- responses[response['status']] += 1
37
- responses
38
- end.map do |status, indexes|
31
+ @all ||= @tomogram.to_hash.each_with_object([]) do |request, routes|
32
+ responses(request).map do |status, indexes|
39
33
  indexes.times do |index|
40
34
  route = "#{request['method']}\t#{request['path']} #{status} #{index}"
41
35
  routes.push(route)
42
36
  end
43
37
  end
44
- routes
45
38
  end.uniq
46
39
  end
40
+
41
+ def responses(request)
42
+ request['responses'].each_with_object({}) do |response, responses|
43
+ responses[response['status']] ||= 0
44
+ responses[response['status']] += 1
45
+ end
46
+ end
47
+
48
+ def black?(response)
49
+ data = response.split(' ')
50
+ data[1] && !@white_list[data[1]] || (@white_list[data[1]] != [] && !@white_list[data[1]].include?(data[0]))
51
+ end
52
+
53
+ def white?(response)
54
+ data = response.split(' ')
55
+ data[1] && @white_list[data[1]] && (@white_list[data[1]] == [] || @white_list[data[1]].include?(data[0]))
56
+ end
47
57
  end
48
58
  end
@@ -27,12 +27,12 @@ module Fitting
27
27
  "got: #{@response.real_request_with_status}"
28
28
  end
29
29
 
30
- unless @response.fully_validates.valid?
31
- "response does not conform to json-schema\n"\
30
+ return nil if @response.fully_validates.valid?
31
+
32
+ "response does not conform to json-schema\n"\
32
33
  "schemas: \n#{@response.expected}\n\n"\
33
34
  "got: #{@response.got}\n\n"\
34
35
  "errors: \n#{@response.fully_validates}\n"
35
- end
36
36
  end
37
37
  end
38
38
 
@@ -55,12 +55,12 @@ module Fitting
55
55
  "got: #{@response.real_request_with_status}"
56
56
  end
57
57
 
58
- unless @response.strict_fully_validates.valid?
59
- "response does not conform to json-schema\n"\
58
+ return nil if @response.strict_fully_validates.valid?
59
+
60
+ "response does not conform to json-schema\n"\
60
61
  "schemas: \n#{@response.expected}\n\n"\
61
62
  "got: #{@response.got}\n\n"\
62
63
  "errors: \n#{@response.strict_fully_validates}\n"
63
- end
64
64
  end
65
65
  end
66
66
 
@@ -19,10 +19,10 @@ module Fitting
19
19
  end
20
20
 
21
21
  def schemas_of_possible_responses(status:)
22
- if @schema
23
- @schema.find_responses(status: status).map do |response|
24
- response['body']
25
- end
22
+ return nil unless @schema
23
+
24
+ @schema.find_responses(status: status).map do |response|
25
+ response['body']
26
26
  end
27
27
  end
28
28
 
@@ -46,7 +46,7 @@ module Fitting
46
46
 
47
47
  def expected
48
48
  @expected ||= @schemas.inject([]) do |res, schema|
49
- res.push("#{JSON.pretty_generate(schema)}")
49
+ res.push(JSON.pretty_generate(schema).to_s)
50
50
  end.join("\n\n")
51
51
  end
52
52
 
@@ -54,17 +54,13 @@ module Fitting
54
54
 
55
55
  def index
56
56
  @schemas.size.times do |i|
57
- if fully_validates[i] == []
58
- return i
59
- end
57
+ return i if fully_validates[i] == []
60
58
  end
61
59
  end
62
60
 
63
61
  def strict_index
64
62
  @schemas.size.times do |i|
65
- if strict_fully_validates[i] == []
66
- return i
67
- end
63
+ return i if strict_fully_validates[i] == []
68
64
  end
69
65
  end
70
66
  end
@@ -22,12 +22,12 @@ module Fitting
22
22
  @to_s ||= join("\n\n")
23
23
  end
24
24
 
25
- private
26
-
27
- def self.fully_validate(schema, body, strict)
28
- JSON::Validator.fully_validate(schema, body, :strict => strict)
29
- rescue JSON::Schema::UriError
30
- []
25
+ class << self
26
+ def fully_validate(schema, body, strict)
27
+ JSON::Validator.fully_validate(schema, body, strict: strict)
28
+ rescue JSON::Schema::UriError
29
+ []
30
+ end
31
31
  end
32
32
  end
33
33
  end
@@ -10,14 +10,15 @@ module Fitting
10
10
  @responses = Fitting::Route::Responses.new(routes, @coverage)
11
11
  end
12
12
 
13
- def statistics
14
- [@requests.statistics, @responses.statistics].join("\n\n")
15
- end
16
-
17
13
  def statistics_with_conformity_lists
18
- return "All responses are 100% valid! Great job!\n" if @coverage.not_coverage.empty?
14
+ congratulation = 'All responses are 100% valid! Great job!' if @coverage.not_coverage.empty?
19
15
 
20
- [@requests.conformity_lists, statistics].join("\n\n")
16
+ [
17
+ @requests.conformity_lists,
18
+ @requests.statistics,
19
+ @responses.statistics,
20
+ congratulation
21
+ ].compact.join("\n\n")
21
22
  end
22
23
 
23
24
  def errors
@@ -1,147 +1,24 @@
1
1
  require 'multi_json'
2
+ require 'fitting/route/requests/statistics'
3
+ require 'fitting/route/requests/lists'
4
+ require 'fitting/route/requests/coverage'
5
+ require 'fitting/route/requests/combine'
2
6
 
3
7
  module Fitting
4
8
  class Route
5
9
  class Requests
6
10
  def initialize(coverage)
7
- @coverage = coverage
8
- end
9
-
10
- def coverage_statistic
11
- stat = {}
12
- @coverage.coverage.map do |route|
13
- macro_key = route.split(' ')[0..1].join(' ')
14
- micro_key = route.split(' ')[2..3].join(' ')
15
- stat[macro_key] ||= {}
16
- stat[macro_key]['cover'] ||= []
17
- stat[macro_key]['not_cover'] ||= []
18
- stat[macro_key]['cover'].push(micro_key)
19
- stat[macro_key]['all'] ||= []
20
- stat[macro_key]['all'].push("✔ #{route.split(' ')[2..3].join(' ')}")
21
- end
22
- @coverage.not_coverage.map do |route|
23
- macro_key = route.split(' ')[0..1].join(' ')
24
- micro_key = route.split(' ')[2..3].join(' ')
25
- stat[macro_key] ||= {}
26
- stat[macro_key]['cover'] ||= []
27
- stat[macro_key]['not_cover'] ||= []
28
- stat[macro_key]['not_cover'].push(micro_key)
29
- stat[macro_key]['all'] ||= []
30
- stat[macro_key]['all'].push("✖ #{route.split(' ')[2..3].join(' ')}")
31
- end
32
- @stat = stat.inject(
33
- {
34
- 'full cover' => [],
35
- 'partial cover' => [],
36
- 'no cover' => []
37
- }
38
- ) do |res, date|
39
- ratio = date.last['cover_ratio'] =
40
- (date.last['cover'].size.to_f /
41
- (date.last['cover'].size + date.last['not_cover'].size).to_f * 100.0).round(2)
42
- info = {date.first => {
43
- 'cover' => date.last['cover'],
44
- 'not_cover' => date.last['not_cover'],
45
- 'all' => "#{beautiful_output(date.last)}"
46
- }}
47
- if ratio == 100.0
48
- res['full cover'].push(info)
49
- elsif ratio == 0.0
50
- res['no cover'].push(info)
51
- else
52
- res['partial cover'].push(info)
53
- end
54
- path = date.first.split(' ')[1].size / 8
55
- @max ||= 1
56
- if path.size > @max
57
- @max = path.size
58
- end
59
- res
60
- end
61
- end
62
-
63
- def to_hash
64
- @stat ||= coverage_statistic
65
- end
66
-
67
- def fully_implemented
68
- @stat ||= coverage_statistic
69
- @fully_implemented ||= @stat['full cover'].map do |response|
70
- "#{response.first.to_a.first.split(' ').join("\t")}#{"\t"*(@max-response.first.to_a.first.split(' ')[1].size/8)}#{response.first.to_a.last['all']}"
71
- end.sort do |first, second|
72
- first.split("\t")[1] <=> second.split("\t")[1]
73
- end
74
- end
75
-
76
- def partially_implemented
77
- @stat ||= coverage_statistic
78
- @partially_implemented ||= @stat['partial cover'].map do |response|
79
- "#{response.first.to_a.first.split(' ').join("\t")}#{"\t"*(@max-response.first.to_a.first.split(' ')[1].size/8)}#{response.first.to_a.last['all']}"
80
- end.sort do |first, second|
81
- first.split("\t")[1] <=> second.split("\t")[1]
82
- end
83
- end
84
-
85
- def no_implemented
86
- @stat ||= coverage_statistic
87
- @no_implemented ||= @stat['no cover'].map do |response|
88
- "#{response.first.to_a.first.split(' ').join("\t")}#{"\t"*(@max-response.first.to_a.first.split(' ')[1].size/8)}#{response.first.to_a.last['all']}"
89
- end.sort do |first, second|
90
- first.split("\t")[1] <=> second.split("\t")[1]
91
- end
92
- end
93
-
94
- def statistics
95
- @stat ||= coverage_statistic
96
- full_count = @stat.to_hash['full cover'].size
97
- part_count = @stat.to_hash['partial cover'].size
98
- no_count = @stat.to_hash['no cover'].size
99
- total_count = full_count + part_count + no_count
100
- full_percentage = (full_count.to_f / total_count.to_f * 100.0).round(2)
101
- part_percentage = (part_count.to_f / total_count.to_f * 100.0).round(2)
102
- no_percentage = (no_count.to_f / total_count.to_f * 100.0).round(2)
103
-
104
- [
105
- "API requests with fully implemented responses: #{full_count} (#{full_percentage}% of #{total_count}).",
106
- "API requests with partially implemented responses: #{part_count} (#{part_percentage}% of #{total_count}).",
107
- "API requests with no implemented responses: #{no_count} (#{no_percentage}% of #{total_count})."
108
- ].join("\n")
11
+ @combine = Fitting::Route::Requests::Combine.new(Fitting::Route::Requests::Coverage.new(coverage))
12
+ @lists = Fitting::Route::Requests::Lists.new(@combine)
13
+ @statistics = Fitting::Route::Requests::Statistics.new(@combine)
109
14
  end
110
15
 
111
16
  def conformity_lists
112
- @stat ||= coverage_statistic
113
- fully_implemented ||= self.fully_implemented.join("\n")
114
- partially_implemented ||= self.partially_implemented.join("\n")
115
- no_implemented ||= self.no_implemented.join("\n")
116
-
117
- [
118
- ['Fully conforming requests:', fully_implemented].join("\n"),
119
- ['Partially conforming requests:', partially_implemented].join("\n"),
120
- ['Non-conforming requests:', no_implemented].join("\n")
121
- ].join("\n\n")
17
+ @lists.to_s
122
18
  end
123
19
 
124
- private
125
-
126
- def beautiful_output(hash)
127
- methods = {}
128
- res = []
129
- hash['cover'].map do |response|
130
- method, index = response.split(' ')
131
- methods[method] ||= []
132
- methods[method][index.to_i] = {'method' => method, 'cover' => true}
133
- end
134
- hash['not_cover'].map do |response|
135
- method, index = response.split(' ')
136
- methods[method] ||= []
137
- methods[method][index.to_i] = {'method' => method, 'cover' => false}
138
- end
139
- methods.map do |method|
140
- method.last.size.times do |index|
141
- res.push("#{method.last[index]['cover'] ? '✔' : '✖'} #{method.first}")
142
- end
143
- end
144
- res.join(' ')
20
+ def statistics
21
+ @statistics.to_s
145
22
  end
146
23
  end
147
24
  end
@@ -0,0 +1,113 @@
1
+ module Fitting
2
+ class Route
3
+ class Requests
4
+ class Combine
5
+ def initialize(stat)
6
+ @stat = stat
7
+ @full_cover = []
8
+ @partial_cover = []
9
+ @no_cover = []
10
+ end
11
+
12
+ def to_hash
13
+ stat_each
14
+ @to_hash ||= {
15
+ 'full cover' => @full_cover,
16
+ 'partial cover' => @partial_cover,
17
+ 'no cover' => @no_cover
18
+ }
19
+ end
20
+
21
+ def max
22
+ stat_each
23
+ @max
24
+ end
25
+
26
+ def full_cover
27
+ stat_each
28
+ @full_cover
29
+ end
30
+
31
+ def partial_cover
32
+ stat_each
33
+ @partial_cover
34
+ end
35
+
36
+ def no_cover
37
+ stat_each
38
+ @no_cover
39
+ end
40
+
41
+ private
42
+
43
+ def stat_each
44
+ @stat_each ||= @stat.to_hash.map do |date|
45
+ date.last['cover_ratio'] = ratio(date)
46
+ res_cover(ratio(date), info(date))
47
+ path = date.first.split(' ')[1].size / 8
48
+ find_max(path)
49
+ end
50
+ end
51
+
52
+ def find_max(path)
53
+ @max ||= 1
54
+ @max = path.size if path.size > @max
55
+ end
56
+
57
+ def res_cover(ratio, info)
58
+ if ratio == 100.0
59
+ @full_cover.push(info)
60
+ elsif ratio == 0.0
61
+ @no_cover.push(info)
62
+ else
63
+ @partial_cover.push(info)
64
+ end
65
+ end
66
+
67
+ def ratio(date)
68
+ (date.last['cover'].size.to_f /
69
+ (date.last['cover'].size + date.last['not_cover'].size).to_f * 100.0).round(2)
70
+ end
71
+
72
+ def info(date)
73
+ { date.first => {
74
+ 'cover' => date.last['cover'],
75
+ 'not_cover' => date.last['not_cover'],
76
+ 'all' => beautiful_output(date.last).to_s
77
+ } }
78
+ end
79
+
80
+ def beautiful_output(hash)
81
+ methods = {}
82
+ res = []
83
+ methods = beautiful_cover(hash, methods)
84
+ methods = beautiful_not_cover(hash, methods)
85
+ methods.map do |method|
86
+ method.last.size.times do |index|
87
+ res.push("#{method.last[index]['cover'] ? '✔' : '✖'} #{method.first}")
88
+ end
89
+ end
90
+ res.join(' ')
91
+ end
92
+
93
+ def beautiful_cover(hash, methods)
94
+ hash['cover'].map do |response|
95
+ method, index = response.split(' ')
96
+ methods[method] ||= []
97
+ methods[method][index.to_i] = { 'method' => method, 'cover' => true }
98
+ end
99
+ methods
100
+ end
101
+
102
+ def beautiful_not_cover(hash, methods)
103
+ hash['not_cover'].map do |response|
104
+ method, index = response.split(' ')
105
+ methods[method] ||= []
106
+ methods[method][index.to_i] = { 'method' => method, 'cover' => false }
107
+ end
108
+ methods
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,58 @@
1
+ module Fitting
2
+ class Route
3
+ class Requests
4
+ class Coverage
5
+ def initialize(coverage)
6
+ @coverage = coverage
7
+ end
8
+
9
+ def to_hash
10
+ stat = {}
11
+ @coverage.coverage.map do |route|
12
+ stat = coverage_stat(stat, route, '✔')
13
+ end
14
+ @coverage.not_coverage.map do |route|
15
+ stat = not_coverage_stat(stat, route, '✖')
16
+ end
17
+ stat
18
+ end
19
+
20
+ private
21
+
22
+ def coverage_stat(stat, route, symbol)
23
+ macro_key = macro_key(route)
24
+ micro_key = micro_key(route)
25
+ stat = default_stat(stat, macro_key)
26
+ stat[macro_key]['cover'].push(micro_key)
27
+ stat[macro_key]['all'].push("#{symbol} #{route.split(' ')[2..3].join(' ')}")
28
+ stat
29
+ end
30
+
31
+ def not_coverage_stat(stat, route, symbol)
32
+ macro_key = macro_key(route)
33
+ micro_key = micro_key(route)
34
+ stat = default_stat(stat, macro_key)
35
+ stat[macro_key]['not_cover'].push(micro_key)
36
+ stat[macro_key]['all'].push("#{symbol} #{route.split(' ')[2..3].join(' ')}")
37
+ stat
38
+ end
39
+
40
+ def default_stat(stat, macro_key)
41
+ stat[macro_key] ||= {}
42
+ stat[macro_key]['cover'] ||= []
43
+ stat[macro_key]['not_cover'] ||= []
44
+ stat[macro_key]['all'] ||= []
45
+ stat
46
+ end
47
+
48
+ def macro_key(route)
49
+ route.split(' ')[0..1].join(' ')
50
+ end
51
+
52
+ def micro_key(route)
53
+ route.split(' ')[2..3].join(' ')
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,92 @@
1
+ module Fitting
2
+ class Route
3
+ class Requests
4
+ class Lists
5
+ def initialize(combine)
6
+ @full_cover = combine.full_cover
7
+ @partial_cover = combine.partial_cover
8
+ @no_cover = combine.no_cover
9
+ @max = combine.max
10
+ end
11
+
12
+ def to_s
13
+ @to_s ||=
14
+ [
15
+ list_fully_implemented,
16
+ list_partially_implemented,
17
+ list_no_implemented
18
+ ].compact.join("\n\n")
19
+ end
20
+
21
+ def list_fully_implemented
22
+ return nil if fully_implemented == []
23
+ ['Fully conforming requests:', fully_implemented].join("\n")
24
+ end
25
+
26
+ def list_partially_implemented
27
+ return nil if partially_implemented == []
28
+ ['Partially conforming requests:', partially_implemented].join("\n")
29
+ end
30
+
31
+ def list_no_implemented
32
+ return nil if no_implemented == []
33
+ ['Non-conforming requests:', no_implemented].join("\n")
34
+ end
35
+
36
+ def fully_implemented
37
+ @fully_implemented ||= stat_full_cover.sort do |first, second|
38
+ first.split("\t")[1] <=> second.split("\t")[1]
39
+ end
40
+ end
41
+
42
+ def partially_implemented
43
+ @partially_implemented ||= stat_partial_cover.sort do |first, second|
44
+ first.split("\t")[1] <=> second.split("\t")[1]
45
+ end
46
+ end
47
+
48
+ def no_implemented
49
+ @no_implemented ||= stat_no_cover.sort do |first, second|
50
+ first.split("\t")[1] <=> second.split("\t")[1]
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def stat_full_cover
57
+ @full_cover.map do |response|
58
+ cover_request(response)
59
+ end
60
+ end
61
+
62
+ def stat_partial_cover
63
+ @partial_cover.map do |response|
64
+ cover_request(response)
65
+ end
66
+ end
67
+
68
+ def stat_no_cover
69
+ @no_cover.map do |response|
70
+ cover_request(response)
71
+ end
72
+ end
73
+
74
+ def cover_request(response)
75
+ "#{cover_method(response)}#{tabulation(response)}#{cover_status(response)}"
76
+ end
77
+
78
+ def cover_method(response)
79
+ response.first.to_a.first.split(' ').join("\t")
80
+ end
81
+
82
+ def tabulation(response)
83
+ "\t" * (@max - response.first.to_a.first.split(' ')[1].size / 8)
84
+ end
85
+
86
+ def cover_status(response)
87
+ response.first.to_a.last['all']
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,40 @@
1
+ module Fitting
2
+ class Route
3
+ class Requests
4
+ class Statistics
5
+ def initialize(combine)
6
+ @full_count = combine.full_cover.size
7
+ @part_count = combine.partial_cover.size
8
+ @no_count = combine.no_cover.size
9
+ end
10
+
11
+ def to_s
12
+ @to_s ||= [
13
+ "API requests with fully implemented responses: #{@full_count} (#{full_percent}% of #{total_count}).",
14
+ "API requests with partially implemented responses: #{@part_count} (#{part_percent}% of #{total_count}).",
15
+ "API requests with no implemented responses: #{@no_count} (#{no_percent}% of #{total_count})."
16
+ ].join("\n")
17
+ end
18
+
19
+ def total_count
20
+ @total_count ||= @full_count + @part_count + @no_count
21
+ end
22
+
23
+ def full_percent
24
+ @full_percentage ||= 0.0 if total_count.zero?
25
+ @full_percentage ||= (@full_count.to_f / total_count.to_f * 100.0).round(2)
26
+ end
27
+
28
+ def part_percent
29
+ @part_percentage ||= 0.0 if total_count.zero?
30
+ @part_percentage ||= (@part_count.to_f / total_count.to_f * 100.0).round(2)
31
+ end
32
+
33
+ def no_percent
34
+ @no_percentage ||= 0.0 if total_count.zero?
35
+ @no_percentage ||= (@no_count.to_f / total_count.to_f * 100.0).round(2)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -15,7 +15,8 @@ module Fitting
15
15
 
16
16
  [
17
17
  "API responses conforming to the blueprint: #{valid_count} (#{valid_percentage}% of #{total_count}).",
18
- "API responses with validation errors or untested: #{invalid_count} (#{invalid_percentage}% of #{total_count})."
18
+ 'API responses with validation errors or untested: '\
19
+ "#{invalid_count} (#{invalid_percentage}% of #{total_count})."
19
20
  ].join("\n")
20
21
  end
21
22
  end
@@ -10,7 +10,7 @@ module Fitting
10
10
  end
11
11
 
12
12
  def save
13
- FileUtils::mkdir_p 'fitting'
13
+ FileUtils.mkdir_p 'fitting'
14
14
  File.open('fitting/stats', 'w') { |file| file.write(to_s) }
15
15
  File.open('fitting/not_covered', 'w') { |file| file.write(@white_route.errors) }
16
16
  end
@@ -20,7 +20,7 @@ module Fitting
20
20
  [
21
21
  ['[Black list]', @black_route.statistics_with_conformity_lists].join("\n"),
22
22
  ['[White list]', @white_route.statistics_with_conformity_lists].join("\n"),
23
- ""
23
+ ''
24
24
  ].join("\n\n")
25
25
  else
26
26
  [@white_route.statistics_with_conformity_lists, "\n\n"].join
@@ -13,7 +13,7 @@ module Fitting
13
13
  Tomograph::Tomogram.new(
14
14
  prefix: Fitting.configuration.prefix,
15
15
  apib_path: Fitting.configuration.apib_path,
16
- drafter_yaml_path: Fitting.configuration.drafter_yaml_path,
16
+ drafter_yaml_path: Fitting.configuration.drafter_yaml_path
17
17
  )
18
18
  end
19
19
  end
@@ -1,3 +1,5 @@
1
+ require 'fitting/storage/white_list'
2
+
1
3
  module Fitting
2
4
  module Storage
3
5
  class Responses
@@ -9,18 +11,32 @@ module Fitting
9
11
  @responses.push(
10
12
  Fitting::Response.new(
11
13
  response,
12
- Fitting::Storage::Documentation.tomogram))
14
+ Fitting::Storage::Documentation.tomogram
15
+ )
16
+ )
13
17
  end
14
18
 
15
19
  def statistics
20
+ @white_list = white_list
16
21
  Fitting::Statistics.new(
17
22
  Fitting::Documentation.new(
18
23
  Fitting::Storage::Documentation.tomogram,
19
- Fitting.configuration.white_list),
24
+ @white_list
25
+ ),
20
26
  @responses.uniq,
21
27
  Fitting.configuration.strict
22
28
  )
23
29
  end
30
+
31
+ private
32
+
33
+ def white_list
34
+ Fitting::Storage::WhiteList.new(
35
+ Fitting.configuration.white_list,
36
+ Fitting.configuration.resource_white_list,
37
+ Fitting::Storage::Documentation.tomogram.to_resources
38
+ ).to_a
39
+ end
24
40
  end
25
41
  end
26
42
  end
@@ -0,0 +1,51 @@
1
+ require 'tomograph'
2
+
3
+ module Fitting
4
+ module Storage
5
+ class WhiteList
6
+ def initialize(white_list, resource_white_list, resources)
7
+ @white_list = white_list
8
+ @resource_white_list = resource_white_list
9
+ @resources = resources
10
+ end
11
+
12
+ def to_a
13
+ return @white_list if @white_list
14
+ @white_list = transformation
15
+ end
16
+
17
+ def without_group
18
+ @resource_white_list.inject([]) do |all_requests, asd|
19
+ if asd[1] == []
20
+ requests(@resources[asd[0]], all_requests)
21
+ else
22
+ requests(asd[1], all_requests)
23
+ end
24
+ end.flatten.uniq
25
+ end
26
+
27
+ def requests(resource, all_requests)
28
+ resource.map do |request|
29
+ all_requests.push(request_hash(request))
30
+ end
31
+ all_requests
32
+ end
33
+
34
+ def transformation
35
+ result = without_group.group_by { |action| action[:path] }
36
+ result.inject({}) do |res, group|
37
+ methods = group.last.map { |gr| gr[:method] }
38
+ res.merge(group.first => methods)
39
+ end
40
+ end
41
+
42
+ def request_hash(request)
43
+ array = request.split(' ')
44
+ {
45
+ method: array[0],
46
+ path: Tomograph::Path.new(array[1]).to_s
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,3 +1,3 @@
1
1
  module Fitting
2
- VERSION = '2.0.3'.freeze
2
+ VERSION = '2.1.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fitting
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - d.efimov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-05-02 00:00:00.000000000 Z
11
+ date: 2017-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json-schema
@@ -50,20 +50,20 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '1.0'
53
+ version: '1.1'
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: 1.0.0
56
+ version: 1.1.0
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '1.0'
63
+ version: '1.1'
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: 1.0.0
66
+ version: 1.1.0
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: bundler
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -201,10 +201,15 @@ files:
201
201
  - lib/fitting/route.rb
202
202
  - lib/fitting/route/coverage.rb
203
203
  - lib/fitting/route/requests.rb
204
+ - lib/fitting/route/requests/combine.rb
205
+ - lib/fitting/route/requests/coverage.rb
206
+ - lib/fitting/route/requests/lists.rb
207
+ - lib/fitting/route/requests/statistics.rb
204
208
  - lib/fitting/route/responses.rb
205
209
  - lib/fitting/statistics.rb
206
210
  - lib/fitting/storage/documentation.rb
207
211
  - lib/fitting/storage/responses.rb
212
+ - lib/fitting/storage/white_list.rb
208
213
  - lib/fitting/version.rb
209
214
  homepage: https://github.com/funbox/fitting
210
215
  licenses: