swagcov 0.2.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a764a7d06c6d8217807646056aa78f72a0a1bec7c289c3eea9f1fc49fc118d44
4
- data.tar.gz: a5b592e359fc41b8dbd7ddb692d7f8b656bba1474c5b397159a5b0a32a096040
3
+ metadata.gz: 5aaf6a7a919c70669dc24a129159b2ce961aa237495d57cfd1d94350bbad526f
4
+ data.tar.gz: e312bf832ceca1bc66017b131c958ab9f70ea4efddc32fac9bda0fcb369d7696
5
5
  SHA512:
6
- metadata.gz: 1add2c26c08559ccc88a42739747676176b0c0d86e18c6086fceeaf82f44f418f1c63a7bb840a5d954be79a93d187d14a84a04a2e09059242bbeed27738beed7
7
- data.tar.gz: c15c67b17352ffc5be4fa6b4ae585647f9b217eb5620d9f534e9b1ab8903dbd859bbe656dc473f5e046dfc5f5f69265eba4b1b27f6dce426aa1ff374ee8d2949
6
+ metadata.gz: 14de18f74737e418b16153aea8c9f107545b9445fc5956dd0617ddbae4a0ad78be83017d6df8f442724186d38fd44a47b8462d4dc7ce2590c2b7aae26aaff201
7
+ data.tar.gz: b22dc442442935535df66b494d9f24f4475517eb36468208d24a3ad3b43f49202eb6ada1679efc1657804ea2e789b62b8dac030009ad1bf282fc9b6b3cfa1853
data/README.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Swagcov
2
- See coverage report for openapi docs for Rails Routes.
2
+ [![Gem Version](https://img.shields.io/gem/v/swagcov)](https://rubygems.org/gems/swagcov)
3
+ ![Gem Downloads](https://img.shields.io/gem/dt/swagcov)
4
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop-hq/rubocop)
5
+ [![GitHub License](https://img.shields.io/github/license/smridge/swagcov.svg)](https://github.com/smridge/swagcov/blob/main/LICENSE)
6
+
7
+ See OpenAPI documentation coverage report for Rails Routes.
8
+
9
+ ## Usages
10
+ - See overview of different endpoints covered, missing and what you choose to ignore.
11
+ - Add pass/fail to your build pipeline when missing Documentation Coverage.
3
12
 
4
13
  ## Installation
5
14
  Add this line to your application's Gemfile:
@@ -34,10 +43,9 @@ Create a `.swagcov.yml` in root of your Rails application.
34
43
  paths:
35
44
  ignore:
36
45
  - /v1/foobar/:token
37
- - /sidekiq
38
46
  ```
39
47
 
40
- - Example `.swagcov.yml` Config File:
48
+ - Full Example `.swagcov.yml` Config File:
41
49
  ```yml
42
50
  docs:
43
51
  paths:
@@ -49,7 +57,6 @@ Create a `.swagcov.yml` in root of your Rails application.
49
57
  - ^/v1
50
58
  ignore:
51
59
  - /v1/foobar/:token
52
- - /sidekiq
53
60
  ```
54
61
 
55
62
  Execute:
@@ -57,6 +64,44 @@ Execute:
57
64
  bundle exec rake swagcov
58
65
  ```
59
66
 
67
+ ### Example configurations and output from running `bundle exec rake swagcov` from the root of your Rails Application:
68
+ - All Routes (minimal configuration):
69
+ ```yml
70
+ docs:
71
+ paths:
72
+ - swagger.yaml
73
+ ```
74
+ <img src="https://raw.githubusercontent.com/smridge/swagcov/main/images/all-endpoints.png">
75
+
76
+
77
+ - With `only` endpoint configuration:
78
+ ```yml
79
+ docs:
80
+ paths:
81
+ - swagger.yaml
82
+
83
+ routes:
84
+ paths:
85
+ only:
86
+ - ^/v2
87
+ ```
88
+ <img src="https://raw.githubusercontent.com/smridge/swagcov/main/images/only-endpoints.png">
89
+
90
+ - With `ignore` and `only` endpoint configurations:
91
+ ```yml
92
+ docs:
93
+ paths:
94
+ - swagger/v1/swagger.yaml
95
+
96
+ routes:
97
+ paths:
98
+ only:
99
+ - ^/v2
100
+ ignore:
101
+ - /v2/users
102
+ ```
103
+ <img src="https://raw.githubusercontent.com/smridge/swagcov/main/images/ignore-and-only-endpoints.png">
104
+
60
105
  ## Development
61
106
  ```shell
62
107
  git clone git@github.com:smridge/swagcov.git
@@ -72,18 +117,39 @@ gem "swagcov", path: "../swagcov"
72
117
  bundle
73
118
  ```
74
119
 
120
+ Run Tests
121
+ ```
122
+ bundle exec rspec spec --exclude-pattern spec/sandbox_**/**/*_spec.rb
123
+ ```
124
+
125
+ ### Test via Sandbox Application
126
+ - Go to any sandbox application within the `spec` directory
127
+ - For example, `cd spec/sandbox_5_2/`
128
+ - Install the ruby version specified in `.ruby-version` file
129
+ - Install gems: `bundle install`
130
+ - Run tests: `bundle exec rspec spec`
131
+ - Run `bundle exec rake swagcov`
132
+ - This will run against any changes made to your branch.
133
+
75
134
  ## Publish (internal)
76
135
  > Note: Publishing a new version of this gem is only meant for maintainers.
77
136
  - Ensure you have access to publish on [rubygems](https://rubygems.org/gems/swagcov).
78
137
  - Update [CHANGELOG](https://github.com/smridge/swagcov/blob/main/CHANGELOG.md).
79
138
  - Update [`VERSION`](https://github.com/smridge/swagcov/blob/main/lib/swagcov/version.rb).
139
+ - Commit changes to `main` branch locally.
80
140
  - Run: `rake release`
81
141
  - This command builds the gem, creates a tag and publishes to rubygems, see [bundler docs](https://bundler.io/guides/creating_gem.html#releasing-the-gem).
82
142
 
83
143
  ## TODO
84
- - Add specs
85
- - Test against different rails versions
144
+ - Create Rails 7 / Ruby 3.1 Sandbox
86
145
  - Add autogeneration of ignore paths
146
+ - Add `CONTRIBUTING.md`
147
+ - Add GitHub Action for linting
87
148
 
88
149
  ## Credit
89
150
  To [@lonelyelk](https://github.com/lonelyelk) for initial development!
151
+
152
+ ## Contributors
153
+ <a href="https://github.com/smridge/swagcov/graphs/contributors">
154
+ <img src="https://contrib.rocks/image?repo=smridge/swagcov" />
155
+ </a>
@@ -2,37 +2,51 @@
2
2
 
3
3
  module Swagcov
4
4
  class Coverage
5
- def initialize
5
+ attr_reader :total, :covered, :ignored, :routes_not_covered, :routes_covered, :routes_ignored
6
+
7
+ def initialize dotfile: Swagcov::Dotfile.new, routes: ::Rails.application.routes.routes
6
8
  @total = 0
7
9
  @covered = 0
8
10
  @ignored = 0
9
11
  @routes_not_covered = []
10
12
  @routes_covered = []
11
13
  @routes_ignored = []
14
+ @dotfile = dotfile
15
+ @routes = routes
12
16
  end
13
17
 
14
18
  def report
15
- Rails.application.routes.routes.each do |route|
16
- # https://github.com/rails/rails/blob/48f3c3e201b57a4832314b2c957a3b303e89bfea/actionpack/lib/action_dispatch/routing/inspector.rb#L105-L107
17
- # Skips route paths like ["/rails/info/properties", "/rails/info", "/rails/mailers"]
18
- next if route.internal
19
+ collect_coverage
20
+ routes_output(@routes_covered, "green")
21
+ routes_output(@routes_ignored, "yellow")
22
+ routes_output(@routes_not_covered, "red")
19
23
 
20
- # Skips routes like "/sidekiq"
21
- next unless route.verb.present?
24
+ final_output
25
+
26
+ @total - @covered
27
+ end
22
28
 
29
+ private
30
+
31
+ attr_reader :dotfile
32
+
33
+ def collect_coverage
34
+ @routes.each do |route|
23
35
  path = route.path.spec.to_s.sub(/\(\.:format\)$/, "")
24
36
 
25
- if ignore_path?(path)
37
+ next if third_party_route?(route, path)
38
+
39
+ if dotfile.ignore_path?(path)
26
40
  @ignored += 1
27
41
  @routes_ignored << { verb: route.verb, path: path, status: "ignored" }
28
42
  next
29
43
  end
30
44
 
31
- next if only_path_mismatch?(path)
45
+ next if dotfile.only_path_mismatch?(path)
32
46
 
33
47
  @total += 1
34
- regex = Regexp.new("#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}(\\.[^/]+)?$")
35
- matching_keys = docs_paths.keys.select { |k| regex.match?(k) }
48
+ regex = Regexp.new("^#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}(\\.[^/]+)?$")
49
+ matching_keys = docs_paths.keys.grep(regex)
36
50
 
37
51
  if (doc = docs_paths.dig(matching_keys.first, route.verb.downcase))
38
52
  @covered += 1
@@ -41,57 +55,39 @@ module Swagcov
41
55
  @routes_not_covered << { verb: route.verb, path: path, status: "none" }
42
56
  end
43
57
  end
44
-
45
- routes_output(@routes_covered, "green")
46
- routes_output(@routes_ignored, "yellow")
47
- routes_output(@routes_not_covered, "red")
48
-
49
- final_output
50
-
51
- exit @total - @covered
52
- end
53
-
54
- def dotfile
55
- @dotfile ||= YAML.load_file(Rails.root.join(".swagcov.yml"))
56
58
  end
57
59
 
58
60
  def docs_paths
59
- @docs_paths ||= Dir.glob(dotfile["docs"]["paths"]).reduce({}) do |acc, docspath|
60
- acc.merge(YAML.load_file(docspath)["paths"])
61
+ @docs_paths ||= Dir.glob(dotfile.doc_paths).reduce({}) do |acc, docspath|
62
+ acc.merge(load_yaml(docspath))
61
63
  end
62
64
  end
63
65
 
64
- private
65
-
66
- def ignore_path? path
67
- ignored_regex&.match?(path)
66
+ def load_yaml docspath
67
+ YAML.load_file(docspath)["paths"]
68
+ rescue Psych::SyntaxError
69
+ raise BadConfigurationError, "Malinformed openapi file (#{docspath})"
68
70
  end
69
71
 
70
- def ignored_regex
71
- @ignored_regex ||= path_config_regex(dotfile.dig("routes", "paths", "ignore"))
72
- end
73
-
74
- def only_path_mismatch? path
75
- only_regex && !only_regex.match?(path)
76
- end
72
+ def third_party_route? route, path
73
+ # https://github.com/rails/rails/blob/48f3c3e201b57a4832314b2c957a3b303e89bfea/actionpack/lib/action_dispatch/routing/inspector.rb#L105-L107
74
+ # Skips route paths like ["/rails/info/properties", "/rails/info", "/rails/mailers"]
75
+ route.internal ||
77
76
 
78
- def only_regex
79
- @only_regex ||= path_config_regex(dotfile.dig("routes", "paths", "only"))
80
- end
81
-
82
- def path_config_regex path_config
83
- return unless path_config
84
-
85
- config = path_config.map { |path| path.first == "^" ? path : "#{path}$" }
77
+ # Skips routes like "/sidekiq"
78
+ route.verb.blank? ||
86
79
 
87
- /#{config.join('|')}/
80
+ # Exclude routes that are part of the rails gem that you would not write documentation for
81
+ # https://github.com/rails/rails/tree/main/activestorage/app/controllers/active_storage
82
+ # https://github.com/rails/rails/tree/main/actionmailbox/app/controllers/action_mailbox
83
+ path.include?("/active_storage/") || path.include?("/action_mailbox/")
88
84
  end
89
85
 
90
86
  def routes_output routes, status_color
91
87
  routes.each do |route|
92
88
  $stdout.puts(
93
89
  format(
94
- "%<verb>10s %<path>-90s %<status>s",
90
+ "%<verb>10s %<path>-90s %<status>s",
95
91
  { verb: route[:verb], path: route[:path], status: route[:status].send(status_color) }
96
92
  )
97
93
  )
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Swagcov
4
+ class BadConfigurationError < RuntimeError
5
+ end
6
+
7
+ class Dotfile
8
+ DEFAULT_CONFIG_FILE_NAME = ".swagcov.yml"
9
+
10
+ def initialize pathname: ::Rails.root.join(DEFAULT_CONFIG_FILE_NAME)
11
+ @dotfile = load_yaml(pathname)
12
+
13
+ raise BadConfigurationError, "Invalid config file (#{DEFAULT_CONFIG_FILE_NAME})" unless valid?
14
+ end
15
+
16
+ def ignore_path? path
17
+ ignored_regex&.match?(path)
18
+ end
19
+
20
+ def only_path_mismatch? path
21
+ only_regex && !only_regex.match?(path)
22
+ end
23
+
24
+ def doc_paths
25
+ dotfile.dig("docs", "paths")
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :dotfile
31
+
32
+ def load_yaml pathname
33
+ raise BadConfigurationError, "Missing config file (#{DEFAULT_CONFIG_FILE_NAME})" unless pathname.exist?
34
+
35
+ YAML.load_file(pathname)
36
+ rescue Psych::SyntaxError
37
+ raise BadConfigurationError, "Malinformed config file (#{DEFAULT_CONFIG_FILE_NAME})"
38
+ end
39
+
40
+ def ignored_regex
41
+ @ignored_regex ||= path_config_regex(dotfile.dig("routes", "paths", "ignore"))
42
+ end
43
+
44
+ def only_regex
45
+ @only_regex ||= path_config_regex(dotfile.dig("routes", "paths", "only"))
46
+ end
47
+
48
+ def path_config_regex path_config
49
+ return unless path_config
50
+
51
+ config = path_config.map { |path| path.first == "^" ? path : "^#{path}$" }
52
+
53
+ /#{config.join('|')}/
54
+ end
55
+
56
+ def valid?
57
+ dotfile && doc_paths
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Swagcov
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/swagcov.rb CHANGED
@@ -4,6 +4,7 @@ require "swagcov/version"
4
4
 
5
5
  if defined?(Rails)
6
6
  require "swagcov/railtie"
7
+ require "swagcov/dotfile"
7
8
  require "swagcov/coverage"
8
9
  end
9
10
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  desc "check documentation coverage for endpoints"
4
4
  task swagcov: :environment do
5
- Swagcov::Coverage.new.report
5
+ exit Swagcov::Coverage.new.report
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swagcov
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sarah Ridge
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-22 00:00:00.000000000 Z
11
+ date: 2022-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry-byebug
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'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rspec
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,20 @@ dependencies:
66
80
  - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  description:
70
98
  email:
71
99
  - sarahmarie@hey.com
@@ -79,6 +107,7 @@ files:
79
107
  - lib/swagcov.rb
80
108
  - lib/swagcov/core_ext/string.rb
81
109
  - lib/swagcov/coverage.rb
110
+ - lib/swagcov/dotfile.rb
82
111
  - lib/swagcov/railtie.rb
83
112
  - lib/swagcov/version.rb
84
113
  - lib/tasks/swagcove.rake
@@ -90,6 +119,7 @@ metadata:
90
119
  homepage_uri: https://github.com/smridge/swagcov
91
120
  source_code_uri: https://github.com/smridge/swagcov
92
121
  changelog_uri: https://github.com/smridge/swagcov/blob/main/CHANGELOG.md
122
+ rubygems_mfa_required: 'true'
93
123
  post_install_message:
94
124
  rdoc_options: []
95
125
  require_paths:
@@ -105,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
135
  - !ruby/object:Gem::Version
106
136
  version: '0'
107
137
  requirements: []
108
- rubygems_version: 3.2.3
138
+ rubygems_version: 3.2.32
109
139
  signing_key:
110
140
  specification_version: 4
111
141
  summary: Open API docs coverage for Rails Routes