swagcov 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c84f31d41e49c7d90290cbdc863d512896100660cf777b66fc2a41837dafadf5
4
+ data.tar.gz: 87af9e2410c54d994f4c6d2832b7a905216aba3a4d25a27691ec49f78c8f5715
5
+ SHA512:
6
+ metadata.gz: 28db77ab060836b1b98a4e38de339b96f613de5d954f67050840f722a0d6b484c2d12da003479999ebcee42d506963826a776a28faaa85bb9d437b4a44e178cb
7
+ data.tar.gz: 382179ddd4f08e04823f0c9bf2f2f8925ba1929bc12d6baadb0882de172684a8361d95fddb55b66ab5669e76cc3d6062bac9cf3f5c1a0d4149273fa27d35b6ab
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2021 Sarah Ridge
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Swagcov
2
+ See coverage report for openapi docs for Rails Routes.
3
+
4
+ ## Installation
5
+ Add this line to your application's Gemfile:
6
+ ```ruby
7
+ gem "swagcov"
8
+ ```
9
+
10
+ Execute:
11
+ ```shell
12
+ bundle
13
+ ```
14
+
15
+ Create a `.swagcov.yml` in root of your Rails application.
16
+ - Add the paths of your `openapi` yml files (**required**):
17
+ ```yml
18
+ docs:
19
+ paths:
20
+ - swagger/v1/swagger.yaml
21
+ ```
22
+
23
+ - Add `only` routes (**optional**) :
24
+ ```yml
25
+ routes:
26
+ paths:
27
+ only:
28
+ - ^/v1
29
+ ```
30
+
31
+ - Add `ignore` routes (**optional**) :
32
+ ```yml
33
+ routes:
34
+ paths:
35
+ ignore:
36
+ - /v1/foobar/:token
37
+ - /sidekiq
38
+ ```
39
+
40
+ - Example `.swagcov.yml` Config File:
41
+ ```yml
42
+ docs:
43
+ paths:
44
+ - swagger/v1/swagger.yaml
45
+
46
+ routes:
47
+ paths:
48
+ only:
49
+ - ^/v1
50
+ ignore:
51
+ - /v1/foobar/:token
52
+ - /sidekiq
53
+ ```
54
+
55
+ Execute:
56
+ ```shell
57
+ bundle exec rake swagcov
58
+ ```
59
+
60
+ ## Development
61
+ ```shell
62
+ git clone git@github.com:smridge/swagcov.git
63
+ ```
64
+
65
+ Add this line to your application's Gemfile:
66
+ ```ruby
67
+ # Use the relative path from your application, to the swagcov folder
68
+ gem "swagcov", path: "../swagcov"
69
+ ```
70
+
71
+ ```shell
72
+ bundle
73
+ ```
74
+
75
+ ## TODO
76
+ - Add specs
77
+ - Test against different rails versions
78
+ - Add autogeneration of ignore paths
79
+
80
+ ## Credit
81
+ To [@lonelyelk](https://github.com/lonelyelk) for initial development!
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
data/lib/swagcov.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "swagcov/version"
4
+
5
+ if defined?(Rails)
6
+ require "swagcov/railtie"
7
+ require "swagcov/coverage"
8
+ end
9
+
10
+ module Swagcov
11
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Swagcov
4
+ class Coverage
5
+ def initialize
6
+ @total = 0
7
+ @covered = 0
8
+ @ignored = 0
9
+ @routes_not_covered = []
10
+ @routes_covered = []
11
+ @routes_ignored = []
12
+ end
13
+
14
+ def report
15
+ Rails.application.routes.routes.each do |route|
16
+ path = route.path.spec.to_s.sub(/\(\.:format\)$/, "")
17
+
18
+ if ignore_path?(path)
19
+ @ignored += 1
20
+ @routes_ignored << { verb: route.verb, path: path, status: "ignored" }
21
+ next
22
+ end
23
+
24
+ next if only_path_mismatch?(path)
25
+
26
+ @total += 1
27
+ regex = Regexp.new("#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}(\\.[^/]+)?$")
28
+ matching_keys = docs_paths.keys.select { |k| regex.match?(k) }
29
+
30
+ if (doc = docs_paths.dig(matching_keys.first, route.verb.downcase))
31
+ @covered += 1
32
+ @routes_covered << { verb: route.verb, path: path, status: doc["responses"].keys.map(&:to_s).sort.join(" ") }
33
+ else
34
+ @routes_not_covered << { verb: route.verb, path: path, status: "none" }
35
+ end
36
+ end
37
+
38
+ routes_output(@routes_covered, "green")
39
+ routes_output(@routes_ignored, "yellow")
40
+ routes_output(@routes_not_covered, "red")
41
+
42
+ final_output
43
+
44
+ (@total - @covered).zero?
45
+ end
46
+
47
+ def dotfile
48
+ @dotfile ||= YAML.load_file(Rails.root.join(".swagcov.yml"))
49
+ end
50
+
51
+ def docs_paths
52
+ @docs_paths ||= Dir.glob(dotfile["docs"]["paths"]).reduce({}) do |acc, docspath|
53
+ acc.merge(YAML.load_file(docspath)["paths"])
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def ignore_path? path
60
+ ignored_regex&.match?(path)
61
+ end
62
+
63
+ def ignored_regex
64
+ @ignored_regex ||= path_config_regex(dotfile.dig("routes", "paths", "ignore"))
65
+ end
66
+
67
+ def only_path_mismatch? path
68
+ only_regex && !only_regex.match?(path)
69
+ end
70
+
71
+ def only_regex
72
+ @only_regex ||= path_config_regex(dotfile.dig("routes", "paths", "only"))
73
+ end
74
+
75
+ def path_config_regex path_config
76
+ return unless path_config
77
+
78
+ config = path_config.map { |path| path.first == "^" ? path : "#{path}$" }
79
+
80
+ /#{config.join('|')}/
81
+ end
82
+
83
+ def routes_output routes, status_color
84
+ routes.each do |route|
85
+ $stdout.puts(
86
+ format(
87
+ "%<verb>10s %<path>-90s %<status>s",
88
+ { verb: route[:verb], path: route[:path], status: route[:status].send(status_color) }
89
+ )
90
+ )
91
+ end
92
+ end
93
+
94
+ def final_output
95
+ $stdout.puts
96
+ $stdout.puts(
97
+ format(
98
+ "OpenAPI documentation coverage %<percentage>.2f%% (%<covered>d/%<total>d)",
99
+ { percentage: 100.0 * @covered / @total, covered: @covered, total: @total }
100
+ )
101
+ )
102
+
103
+ $stdout.puts(
104
+ format(
105
+ "%<total>s endpoints ignored",
106
+ { total: @ignored.to_s.yellow }
107
+ )
108
+ )
109
+
110
+ $stdout.puts(
111
+ format(
112
+ "%<total>s endpoints checked",
113
+ { total: @total.to_s.blue }
114
+ )
115
+ )
116
+
117
+ $stdout.puts(
118
+ format(
119
+ "%<covered>s endpoints covered",
120
+ { covered: @covered.to_s.green }
121
+ )
122
+ )
123
+
124
+ $stdout.puts(
125
+ format(
126
+ "%<missing>s endpoints missing documentation",
127
+ { missing: (@total - @covered).to_s.red }
128
+ )
129
+ )
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Swagcov
4
+ class Railtie < ::Rails::Railtie
5
+ rake_tasks do
6
+ path = File.expand_path("..", __dir__)
7
+
8
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Swagcov
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "check documentation coverage for endpoints"
4
+ task swagcov: :environment do
5
+ Swagcov::Coverage.new.report
6
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: swagcov
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sarah Ridge
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-performance
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - sarahmarie@hey.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - lib/swagcov.rb
80
+ - lib/swagcov/coverage.rb
81
+ - lib/swagcov/railtie.rb
82
+ - lib/swagcov/version.rb
83
+ - lib/tasks/swagcove.rake
84
+ homepage: https://github.com/smridge/swagcov
85
+ licenses:
86
+ - MIT
87
+ metadata:
88
+ allowed_push_host: https://rubygems.org
89
+ homepage_uri: https://github.com/smridge/swagcov
90
+ source_code_uri: https://github.com/smridge/swagcov
91
+ changelog_uri: https://github.com/smridge/swagcov/blob/main/CHANGELOG.md
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 2.5.0
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.2.3
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Open API docs coverage for Rails Routes
111
+ test_files: []