swagcov 0.2.4 → 0.4.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
  SHA256:
3
- metadata.gz: 83f05163f3408b08db9f9810e5eb82a4285877d9e7b7af60a83e44386eab5c04
4
- data.tar.gz: 76b1bf03e51bc42e516f5b77536d1ae38685dd642823094a0ac8e67fa8c67ac3
3
+ metadata.gz: 8aec9444a5e8dfd1d4b23fc61c710d4ccc28d5c7985df0fbef5e8c02e16e7519
4
+ data.tar.gz: a19a3c8f73ad9efb814ff7469b6306bacb52d296893130b7fb42740d1d7e7db4
5
5
  SHA512:
6
- metadata.gz: 82610986597a8a832173354fb452a4d63c7c0a22a342ec10fd2ac6bcb3eef845d306c6d37225ac837b22febcb12a0ba093167b71d3acc94aaabb8394789b77a7
7
- data.tar.gz: a1cdf7691af9fce07dfc50711a748bcbfe903ef0f5f5b94e3d1b3d7fd605ac125de92db23121a8ccdb8622494bfba431f7f3710e55f6392a20160e5c2bfd4e4f
6
+ metadata.gz: cc64199a92c0322fe3c42bbaa882d103a9a8f18e1c3dbe02ddb5b56bb21d13eae763536fbcdb2f84aa8a045fb6d92726f1a953eaa27595118cbe6a7324dd5c2a
7
+ data.tar.gz: 9713fa38210e7f6273e2f9004a7dc791994ebd80e50e0852381de005f67b1c238ce995060c3643b2b1db7520d46687defa554067e3ff2a75ae71ec70a809f496
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:
@@ -17,7 +26,7 @@ Create a `.swagcov.yml` in root of your Rails application.
17
26
  ```yml
18
27
  docs:
19
28
  paths:
20
- - swagger/v1/swagger.yaml
29
+ - swagger.yaml
21
30
  ```
22
31
 
23
32
  - Add `only` routes (**optional**) :
@@ -34,14 +43,13 @@ 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:
44
- - swagger/v1/swagger.yaml
52
+ - swagger.yaml
45
53
 
46
54
  routes:
47
55
  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.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,22 +117,41 @@ 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).
80
- - Commit changes to `main` branch locally.
139
+ - Run `bundle update` for each sandbox application to reflect new swagcov version in each `Gemfile.lock`
140
+ - Open a Pull Request to ensure all specs pass, then merge to `main`.
141
+ - Checkout the latest `main` on your machine.
81
142
  - Run: `rake release`
82
143
  - 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).
83
144
 
84
145
  ## TODO
85
- - Add specs
86
- - Test against different rails versions
87
- - Create Sandbox Apps for Rails 5 & 6
146
+ - Create Rails 7 / Ruby 3.1 Sandbox
88
147
  - Add autogeneration of ignore paths
89
148
  - Add `CONTRIBUTING.md`
90
- - Add GitHub Actions for specs/linting
149
+ - Add GitHub Action for linting
91
150
 
92
151
  ## Credit
93
152
  To [@lonelyelk](https://github.com/lonelyelk) for initial development!
153
+
154
+ ## Contributors
155
+ <a href="https://github.com/smridge/swagcov/graphs/contributors">
156
+ <img src="https://contrib.rocks/image?repo=smridge/swagcov" />
157
+ </a>
@@ -2,31 +2,41 @@
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 = []
12
- @dotfile = Swagcov::Dotfile.new
14
+ @dotfile = dotfile
15
+ @routes = routes
13
16
  end
14
17
 
15
18
  def report
16
- Rails.application.routes.routes.each do |route|
17
- # https://github.com/rails/rails/blob/48f3c3e201b57a4832314b2c957a3b303e89bfea/actionpack/lib/action_dispatch/routing/inspector.rb#L105-L107
18
- # Skips route paths like ["/rails/info/properties", "/rails/info", "/rails/mailers"]
19
- 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")
20
23
 
21
- # Skips routes like "/sidekiq"
22
- next unless route.verb.present?
24
+ final_output
23
25
 
24
- path = route.path.spec.to_s.sub(/\(\.:format\)$/, "")
26
+ @total - @covered
27
+ end
25
28
 
26
- # Exclude routes that are part of the rails gem that you would not write documentation for
27
- # https://github.com/rails/rails/tree/main/activestorage/app/controllers/active_storage
28
- # https://github.com/rails/rails/tree/main/actionmailbox/app/controllers/action_mailbox
29
- next if path.include?("/active_storage/") || path.include?("/action_mailbox/")
29
+ private
30
+
31
+ attr_reader :dotfile
32
+
33
+ def collect_coverage
34
+ openapi_files = ::Swagcov::OpenapiFiles.new(filepaths: dotfile.doc_paths)
35
+
36
+ @routes.each do |route|
37
+ path = route.path.spec.to_s.chomp("(.:format)")
38
+
39
+ next if third_party_route?(route, path)
30
40
 
31
41
  if dotfile.ignore_path?(path)
32
42
  @ignored += 1
@@ -37,41 +47,35 @@ module Swagcov
37
47
  next if dotfile.only_path_mismatch?(path)
38
48
 
39
49
  @total += 1
40
- regex = Regexp.new("#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}(\\.[^/]+)?$")
41
- matching_keys = docs_paths.keys.select { |k| regex.match?(k) }
42
50
 
43
- if (doc = docs_paths.dig(matching_keys.first, route.verb.downcase))
51
+ if (response_keys = openapi_files.find_response_keys(path: path, route_verb: route.verb))
44
52
  @covered += 1
45
- @routes_covered << { verb: route.verb, path: path, status: doc["responses"].keys.map(&:to_s).sort.join(" ") }
53
+ @routes_covered << { verb: route.verb, path: path, status: response_keys.join(" ") }
46
54
  else
47
55
  @routes_not_covered << { verb: route.verb, path: path, status: "none" }
48
56
  end
49
57
  end
50
-
51
- routes_output(@routes_covered, "green")
52
- routes_output(@routes_ignored, "yellow")
53
- routes_output(@routes_not_covered, "red")
54
-
55
- final_output
56
-
57
- exit @total - @covered
58
58
  end
59
59
 
60
- def docs_paths
61
- @docs_paths ||= Dir.glob(dotfile.doc_paths).reduce({}) do |acc, docspath|
62
- acc.merge(YAML.load_file(docspath)["paths"])
63
- end
64
- end
60
+ def third_party_route? route, path
61
+ # https://github.com/rails/rails/blob/48f3c3e201b57a4832314b2c957a3b303e89bfea/actionpack/lib/action_dispatch/routing/inspector.rb#L105-L107
62
+ # Skips route paths like ["/rails/info/properties", "/rails/info", "/rails/mailers"]
63
+ route.internal ||
65
64
 
66
- private
65
+ # Skips routes like "/sidekiq"
66
+ route.verb.blank? ||
67
67
 
68
- attr_reader :dotfile
68
+ # Exclude routes that are part of the rails gem that you would not write documentation for
69
+ # https://github.com/rails/rails/tree/main/activestorage/app/controllers/active_storage
70
+ # https://github.com/rails/rails/tree/main/actionmailbox/app/controllers/action_mailbox
71
+ path.include?("/active_storage/") || path.include?("/action_mailbox/")
72
+ end
69
73
 
70
74
  def routes_output routes, status_color
71
75
  routes.each do |route|
72
76
  $stdout.puts(
73
77
  format(
74
- "%<verb>10s %<path>-90s %<status>s",
78
+ "%<verb>10s %<path>-90s %<status>s",
75
79
  { verb: route[:verb], path: route[:path], status: route[:status].send(status_color) }
76
80
  )
77
81
  )
@@ -5,12 +5,12 @@ module Swagcov
5
5
  end
6
6
 
7
7
  class Dotfile
8
- def initialize
9
- pathname = Rails.root.join(".swagcov.yml")
10
- raise BadConfigurationError, "Missing .swagcov.yml" unless pathname.exist?
8
+ DEFAULT_CONFIG_FILE_NAME = ".swagcov.yml"
11
9
 
12
- @dotfile = YAML.load_file(pathname)
13
- raise BadConfigurationError, "Invalid .swagcov.yml" unless valid?
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
14
  end
15
15
 
16
16
  def ignore_path? path
@@ -29,6 +29,14 @@ module Swagcov
29
29
 
30
30
  attr_reader :dotfile
31
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
+
32
40
  def ignored_regex
33
41
  @ignored_regex ||= path_config_regex(dotfile.dig("routes", "paths", "ignore"))
34
42
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Swagcov
4
+ class OpenapiFiles
5
+ def initialize filepaths:
6
+ @filepaths = filepaths
7
+ @openapi_paths = load_yamls
8
+ end
9
+
10
+ def find_response_keys path:, route_verb:
11
+ # replace :id with {id}
12
+ regex = Regexp.new("^#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}?$")
13
+
14
+ matching_paths_key = @openapi_paths.keys.grep(regex).first
15
+ matching_request_method_key = @openapi_paths.dig(matching_paths_key, route_verb.downcase)
16
+
17
+ matching_request_method_key["responses"].keys.map(&:to_s).sort if matching_request_method_key
18
+ end
19
+
20
+ private
21
+
22
+ def load_yamls
23
+ Dir.glob(@filepaths).reduce({}) do |hash, filepath|
24
+ hash.merge(load_yaml(filepath))
25
+ end
26
+ end
27
+
28
+ def load_yaml filepath
29
+ YAML.load_file(filepath)["paths"]
30
+ rescue Psych::SyntaxError
31
+ raise BadConfigurationError, "Malinformed openapi file (#{filepath})"
32
+ end
33
+ end
34
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Swagcov
4
- VERSION = "0.2.4"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/swagcov.rb CHANGED
@@ -6,6 +6,7 @@ if defined?(Rails)
6
6
  require "swagcov/railtie"
7
7
  require "swagcov/dotfile"
8
8
  require "swagcov/coverage"
9
+ require "swagcov/openapi_files"
9
10
  end
10
11
 
11
12
  require "swagcov/core_ext/string"
@@ -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.4
4
+ version: 0.4.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-30 00:00:00.000000000 Z
11
+ date: 2022-08-12 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
@@ -80,6 +108,7 @@ files:
80
108
  - lib/swagcov/core_ext/string.rb
81
109
  - lib/swagcov/coverage.rb
82
110
  - lib/swagcov/dotfile.rb
111
+ - lib/swagcov/openapi_files.rb
83
112
  - lib/swagcov/railtie.rb
84
113
  - lib/swagcov/version.rb
85
114
  - lib/tasks/swagcove.rake
@@ -91,6 +120,7 @@ metadata:
91
120
  homepage_uri: https://github.com/smridge/swagcov
92
121
  source_code_uri: https://github.com/smridge/swagcov
93
122
  changelog_uri: https://github.com/smridge/swagcov/blob/main/CHANGELOG.md
123
+ rubygems_mfa_required: 'true'
94
124
  post_install_message:
95
125
  rdoc_options: []
96
126
  require_paths:
@@ -106,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
136
  - !ruby/object:Gem::Version
107
137
  version: '0'
108
138
  requirements: []
109
- rubygems_version: 3.1.4
139
+ rubygems_version: 3.2.33
110
140
  signing_key:
111
141
  specification_version: 4
112
142
  summary: Open API docs coverage for Rails Routes