swagcov 0.5.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +71 -31
- data/README.md +29 -4
- data/lib/swagcov/command/generate_dotfile.rb +45 -0
- data/lib/swagcov/command/generate_todo_file.rb +52 -0
- data/lib/swagcov/command/report_coverage.rb +20 -0
- data/lib/swagcov/coverage.rb +37 -90
- data/lib/swagcov/dotfile.rb +40 -31
- data/lib/swagcov/errors.rb +8 -0
- data/lib/swagcov/formatter/console.rb +78 -0
- data/lib/swagcov/openapi_files.rb +5 -5
- data/lib/swagcov/version.rb +3 -1
- data/lib/swagcov.rb +20 -9
- data/lib/tasks/swagcov/generate_todo.rake +8 -0
- data/lib/tasks/swagcov/install.rake +1 -1
- data/lib/tasks/{swagcove.rake → swagcov.rake} +1 -1
- metadata +12 -7
- data/lib/swagcov/install.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1df02b05f23038aacedc8a990a47012d27f90f64f7185c689507face26acd8c4
|
4
|
+
data.tar.gz: 8d2e127ed1b1de208ef87fe6a8920fe505fbdceade1f430300b26e2bb6441d19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4faad26270ff8aceddad224fdb26b6fcd8c86eb0d1c2a75c8bff082aa829187a5d571b7aff037b4a8c5bc907c26c446d55a5807420b111f18abc5c7b475a445c
|
7
|
+
data.tar.gz: a8bbfa8964e97929d4e190353729e399e9f0bf59d78acef28084699e902be2689bf8e7bf2a2ea3289f10114040c10112204c0954da319da4c38971c6ea7f0c5e
|
data/CHANGELOG.md
CHANGED
@@ -2,76 +2,116 @@
|
|
2
2
|
## main (unreleased)
|
3
3
|
-
|
4
4
|
|
5
|
+
## 0.7.0 (2025-04-18)
|
6
|
+
### Enhancement
|
7
|
+
- Add support for Ruby 3.5 ([#98](https://github.com/smridge/swagcov/pull/98))
|
8
|
+
- Add rake task for auto-generation of `.swagcov_todo.yml` file ([#93](https://github.com/smridge/swagcov/pull/93), [#96](https://github.com/smridge/swagcov/pull/96))
|
9
|
+
```shell
|
10
|
+
bundle exec rake swagcov:generate_todo
|
11
|
+
```
|
12
|
+
|
13
|
+
### Refactor
|
14
|
+
- Add `Swagcov.project_root` method ([#86](https://github.com/smridge/swagcov/pull/86))
|
15
|
+
- Separate coverage collection and output ([#83](https://github.com/smridge/swagcov/pull/83))
|
16
|
+
- Replace ActiveSupport string method with ruby string method ([#85](https://github.com/smridge/swagcov/pull/85))
|
17
|
+
- Replace `Swagcov::Coverage.new.report` with `Swagcov::Command::ReportCoverage.new.run` ([#89](https://github.com/smridge/swagcov/pull/89))
|
18
|
+
- Rename `Swagcov::Install` to `Swagcov::Command::GenerateDotfile` ([#88](https://github.com/smridge/swagcov/pull/88))
|
19
|
+
- Refactor `GenerateDotfile` to be consistent with `GenerateTodoFile` ([#97](https://github.com/smridge/swagcov/pull/97))
|
20
|
+
|
21
|
+
### Fix
|
22
|
+
- Add exit code to install task + update messaging ([#87](https://github.com/smridge/swagcov/pull/87))
|
23
|
+
- Ignored path verb matching when duplicate path keys are present ([#92](https://github.com/smridge/swagcov/pull/92))
|
24
|
+
- Fix exit code error handling ([#95](https://github.com/smridge/swagcov/pull/95))
|
25
|
+
|
26
|
+
### Development
|
27
|
+
- Add irb console for local development ([#94](https://github.com/smridge/swagcov/pull/94))
|
28
|
+
|
29
|
+
## 0.6.0 (2025-04-09)
|
30
|
+
### Fix
|
31
|
+
- Grammatical number for endpoint(s) count output ([#78](https://github.com/smridge/swagcov/pull/78))
|
32
|
+
|
33
|
+
### Enhancement
|
34
|
+
- Add support for Rails 4.2 ([#72](https://github.com/smridge/swagcov/pull/72))
|
35
|
+
|
36
|
+
### Refactor
|
37
|
+
- `BREAKING CHANGE`: `Swagcov::BadConfigurationError` is now `Swagcov::Errors::BadConfiguration` ([#74](https://github.com/smridge/swagcov/pull/74))
|
38
|
+
- `BREAKING CHANGE`: `Swagcov::VERSION` is now `Swagcov::Version::STRING` ([#77](https://github.com/smridge/swagcov/pull/77))
|
39
|
+
- Improve path matching processing for `ignore` and `only` routes ([#65](https://github.com/smridge/swagcov/pull/65))
|
40
|
+
|
41
|
+
### Code Coverage
|
42
|
+
- Add test coverage reporting ([#68](https://github.com/smridge/swagcov/pull/68), [#69](https://github.com/smridge/swagcov/pull/69))
|
43
|
+
|
5
44
|
## 0.5.0 (2025-03-26)
|
6
45
|
### Enhancement
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
46
|
+
- Add rake task for configuration installation ([#59](https://github.com/smridge/swagcov/pull/59))
|
47
|
+
```shell
|
48
|
+
bundle exec rake swagcov:install
|
49
|
+
```
|
50
|
+
- Extend `ignore` routes configuration to exclude only specific actions ([#60](https://github.com/smridge/swagcov/pull/60))
|
51
|
+
```yml
|
52
|
+
routes:
|
53
|
+
paths:
|
54
|
+
ignore:
|
55
|
+
- /v2/users # existing configuration that ignores all associated actions (verbs)
|
56
|
+
- /v2/users/:id: # new option to extend to specific actions
|
57
|
+
- GET
|
58
|
+
```
|
20
59
|
|
21
60
|
## 0.4.1 (2025-03-18)
|
22
61
|
### Fix
|
23
62
|
- Output width for better layout alignment ([#48](https://github.com/smridge/swagcov/pull/48))
|
24
63
|
|
25
64
|
### Code Coverage
|
26
|
-
- Added official support for ruby 3.3 and 3.4 and rails 7.1, 7.2, 8.0. See [
|
65
|
+
- Added official support for ruby 3.3 and 3.4 and rails 7.1, 7.2, 8.0. See [tests.yml](/.github/workflows/tests.yml) for detail
|
27
66
|
|
28
67
|
## 0.4.0 (2022-08-11)
|
29
68
|
- Improve OpenAPI file processing ([#26](https://github.com/smridge/swagcov/pull/26))
|
30
69
|
|
31
70
|
## 0.3.0 (2022-02-21)
|
32
71
|
### Enhancement
|
33
|
-
|
72
|
+
- Raise specific `Swagcov::BadConfigurationError` for malformed yaml files ([#23](https://github.com/smridge/swagcov/pull/23))
|
34
73
|
|
35
74
|
### Security
|
36
|
-
|
75
|
+
- Require Multi-Factor Authentication for RubyGems privileged operations ([#16](https://github.com/smridge/swagcov/pull/16))
|
37
76
|
|
38
77
|
### Code Coverage
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
78
|
+
- Add Sandbox Application and specs for Rails 5.1 ([#20](https://github.com/smridge/swagcov/pull/20))
|
79
|
+
- Add specs for Rails 5.2 ([#7](https://github.com/smridge/swagcov/pull/7)), ([#14](https://github.com/smridge/swagcov/pull/14))
|
80
|
+
- Add Sandbox Application and specs for Rails 6.0 ([#17](https://github.com/smridge/swagcov/pull/17))
|
81
|
+
- Add Sandbox Application and specs for Rails 6.1 ([#22](https://github.com/smridge/swagcov/pull/22))
|
82
|
+
- Add GitHub Actions to run specs ([#18](https://github.com/smridge/swagcov/pull/18))
|
83
|
+
- Add GitHub CodeQL ([#13](https://github.com/smridge/swagcov/pull/13))
|
45
84
|
|
46
85
|
### Refactor
|
47
|
-
|
48
|
-
|
86
|
+
- Move `SystemExit` to rake task for easier testing ([#24](https://github.com/smridge/swagcov/pull/24))
|
87
|
+
- Reduce complexity when matching routes ([#15](https://github.com/smridge/swagcov/pull/15))
|
49
88
|
|
50
89
|
## 0.2.5 (2021-09-14)
|
51
90
|
### Fix
|
52
|
-
|
91
|
+
- Matching routes against swagger paths. Previously, partial paths could result in a match ([#12](https://github.com/smridge/swagcov/pull/12))
|
53
92
|
|
54
93
|
## 0.2.4 (2021-04-30)
|
55
94
|
### Fix
|
56
|
-
|
95
|
+
- If a route path does not start with "^" match the entire path ([#5](https://github.com/smridge/swagcov/pull/5))
|
96
|
+
|
57
97
|
### Enhancement
|
58
|
-
|
98
|
+
- Raise specific `Swagcov::BadConfigurationError` error if bad or missing configuration ([#5](https://github.com/smridge/swagcov/pull/5))
|
59
99
|
|
60
100
|
## 0.2.3 (2021-04-23)
|
61
101
|
### Fix
|
62
|
-
|
102
|
+
- Exclude ActiveStorage and ActionMailer routes ([#3](https://github.com/smridge/swagcov/pull/3))
|
63
103
|
|
64
104
|
## 0.2.2 (2021-04-22)
|
65
105
|
### Fix
|
66
|
-
|
106
|
+
- Exclude Rails Internal Routes and Mounted Applications ([#2](https://github.com/smridge/swagcov/pull/2))
|
67
107
|
|
68
108
|
## 0.2.1 (2021-04-21)
|
69
109
|
### Fix
|
70
|
-
|
110
|
+
- Exceptions caused by missing dependency for strings. ([#1](https://github.com/smridge/swagcov/pull/1))
|
71
111
|
|
72
112
|
## 0.2.0 (2021-04-20)
|
73
113
|
### Enhancement
|
74
|
-
|
114
|
+
- Add Exit status to easily build a pass/fail into build pipeline
|
75
115
|
|
76
116
|
## 0.1.0 (2021-02-24)
|
77
117
|
- Create Rake Task for checking documentation coverage (rails only)
|
data/README.md
CHANGED
@@ -4,12 +4,37 @@
|
|
4
4
|
[](https://github.com/rubocop-hq/rubocop)
|
5
5
|
[](https://github.com/smridge/swagcov/blob/main/LICENSE)
|
6
6
|
|
7
|
+

|
8
|
+

|
9
|
+

|
10
|
+
[](https://coveralls.io/github/smridge/swagcov?branch=main)
|
11
|
+
|
7
12
|
See OpenAPI documentation coverage report for Rails Routes.
|
8
13
|
|
9
14
|
## Usages
|
10
15
|
- See overview of different endpoints covered, missing and what you choose to ignore.
|
11
16
|
- Add pass/fail to your build pipeline when missing Documentation Coverage.
|
12
17
|
|
18
|
+
| `rake task` | `rails console` | Description |
|
19
|
+
| :--- | :--- | :--- |
|
20
|
+
| `rake swagcov` | `Swagcov::Command::ReportCoverage.new.run` | Check documentation coverage |
|
21
|
+
| `rake swagcov:install` | `Swagcov::Command::GenerateDotfile.new.run` | Install required `.swagcov.yml` config file |
|
22
|
+
| `rake swagcov:generate_todo` | `Swagcov::Command::GenerateTodoFile.new.run` | Generate `.swagcov_todo.yml` |
|
23
|
+
|
24
|
+
## Ruby and Rails Version Support
|
25
|
+
Versioning support from a test coverage perspective, see [tests.yml](/.github/workflows/tests.yml) for detail
|
26
|
+
| `ruby -v` | `rails 4.2` | `rails 5.0` | `rails 5.1` | `rails 5.2` | `rails 6.0` | `rails 6.1` | `rails 7.0` | `rails 7.1` | `rails 7.2` | `rails 8.0` |
|
27
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
28
|
+
| `2.5` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
29
|
+
| `2.6` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
30
|
+
| `2.7` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
31
|
+
| `3.0` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
32
|
+
| `3.1` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
33
|
+
| `3.2` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
34
|
+
| `3.3` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
35
|
+
| `3.4` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
36
|
+
| `3.5` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
37
|
+
|
13
38
|
## Installation
|
14
39
|
Add this line to your application's Gemfile:
|
15
40
|
```ruby
|
@@ -61,6 +86,8 @@ bundle exec rake swagcov:install
|
|
61
86
|
- ^/v1
|
62
87
|
ignore:
|
63
88
|
- /v1/foobar/:token
|
89
|
+
- /v1/foobar:
|
90
|
+
- GET
|
64
91
|
```
|
65
92
|
|
66
93
|
Execute:
|
@@ -68,7 +95,8 @@ Execute:
|
|
68
95
|
bundle exec rake swagcov
|
69
96
|
```
|
70
97
|
|
71
|
-
|
98
|
+
## Examples
|
99
|
+
Configurations and output from running `bundle exec rake swagcov` from the root of your Rails Application
|
72
100
|
- All Routes (minimal configuration):
|
73
101
|
```yml
|
74
102
|
docs:
|
@@ -108,9 +136,6 @@ bundle exec rake swagcov
|
|
108
136
|
```
|
109
137
|
<img src="https://raw.githubusercontent.com/smridge/swagcov/main/images/ignore-and-only-endpoints.png">
|
110
138
|
|
111
|
-
## TODO
|
112
|
-
- Add autogeneration of ignore paths
|
113
|
-
|
114
139
|
## Contributing
|
115
140
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for detail
|
116
141
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Swagcov
|
4
|
+
module Command
|
5
|
+
class GenerateDotfile
|
6
|
+
attr_reader :dotfile
|
7
|
+
|
8
|
+
def initialize basename: ::Swagcov::Dotfile::DEFAULT_CONFIG_FILE_NAME
|
9
|
+
@dotfile = ::Swagcov.project_root.join(basename)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
if ::File.exist?(@dotfile)
|
14
|
+
$stdout.puts "#{@dotfile.basename} already exists at #{@dotfile.dirname}"
|
15
|
+
return ::Swagcov::STATUS_ERROR
|
16
|
+
end
|
17
|
+
|
18
|
+
::File.write(
|
19
|
+
dotfile,
|
20
|
+
<<~YAML
|
21
|
+
## Required field:
|
22
|
+
# List your OpenAPI documentation files
|
23
|
+
docs:
|
24
|
+
paths:
|
25
|
+
- swagger.yaml
|
26
|
+
|
27
|
+
## Optional fields:
|
28
|
+
# routes:
|
29
|
+
# paths:
|
30
|
+
# only:
|
31
|
+
# - ^/v2 # only track v2 endpoints
|
32
|
+
# ignore:
|
33
|
+
# - /v2/users # do not track certain endpoints
|
34
|
+
# - /v2/users/:id: # ignore only certain actions (verbs)
|
35
|
+
# - GET
|
36
|
+
YAML
|
37
|
+
)
|
38
|
+
|
39
|
+
$stdout.puts "created #{@dotfile.basename} at #{@dotfile.dirname}"
|
40
|
+
|
41
|
+
::Swagcov::STATUS_SUCCESS
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Swagcov
|
4
|
+
module Command
|
5
|
+
class GenerateTodoFile
|
6
|
+
def initialize basename: ::Swagcov::Dotfile::TODO_CONFIG_FILE_NAME,
|
7
|
+
data: ::Swagcov::Coverage.new(dotfile: ::Swagcov::Dotfile.new(skip_todo: true)).collect[:uncovered]
|
8
|
+
@dotfile = ::Swagcov.project_root.join(basename)
|
9
|
+
@data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
::File.write(
|
14
|
+
@dotfile,
|
15
|
+
<<~YAML
|
16
|
+
# This configuration was auto generated
|
17
|
+
# The intent is to remove these route configurations as documentation is added
|
18
|
+
#{routes_yaml}
|
19
|
+
YAML
|
20
|
+
)
|
21
|
+
|
22
|
+
$stdout.puts "created #{@dotfile.basename} at #{@dotfile.dirname}"
|
23
|
+
|
24
|
+
::Swagcov::STATUS_SUCCESS
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def routes_yaml
|
30
|
+
return if routes.empty?
|
31
|
+
|
32
|
+
{
|
33
|
+
"routes" => {
|
34
|
+
"paths" => {
|
35
|
+
"ignore" => routes
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}.to_yaml.strip
|
39
|
+
end
|
40
|
+
|
41
|
+
def routes
|
42
|
+
hash = {}
|
43
|
+
|
44
|
+
@data.each do |route|
|
45
|
+
hash[route[:path]] ? hash[route[:path]] << route[:verb] : hash[route[:path]] = [route[:verb]]
|
46
|
+
end
|
47
|
+
|
48
|
+
@routes ||= hash.map { |key, value| { key => value } }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Swagcov
|
4
|
+
module Command
|
5
|
+
class ReportCoverage
|
6
|
+
def initialize data: nil
|
7
|
+
@data = data
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
data = @data || ::Swagcov::Coverage.new.collect
|
12
|
+
|
13
|
+
::Swagcov::Formatter::Console.new(data: data).run
|
14
|
+
rescue ::Swagcov::Errors::BadConfiguration => e
|
15
|
+
warn "#{e.class}: #{e.message}"
|
16
|
+
::Swagcov::STATUS_ERROR
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/swagcov/coverage.rb
CHANGED
@@ -2,65 +2,57 @@
|
|
2
2
|
|
3
3
|
module Swagcov
|
4
4
|
class Coverage
|
5
|
-
attr_reader :
|
6
|
-
|
7
|
-
def initialize dotfile: Swagcov::Dotfile.new, routes: ::Rails.application.routes.routes
|
8
|
-
@total = 0
|
9
|
-
@covered = 0
|
10
|
-
@ignored = 0
|
11
|
-
@routes_not_covered = []
|
12
|
-
@routes_covered = []
|
13
|
-
@routes_ignored = []
|
5
|
+
attr_reader :dotfile
|
6
|
+
|
7
|
+
def initialize dotfile: ::Swagcov::Dotfile.new, routes: ::Rails.application.routes.routes
|
14
8
|
@dotfile = dotfile
|
15
9
|
@routes = routes
|
10
|
+
@data = {
|
11
|
+
covered: [],
|
12
|
+
ignored: [],
|
13
|
+
uncovered: [],
|
14
|
+
total_count: 0,
|
15
|
+
covered_count: 0,
|
16
|
+
ignored_count: 0,
|
17
|
+
uncovered_count: 0
|
18
|
+
}
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
routes_output(@routes_ignored, "yellow")
|
22
|
-
routes_output(@routes_not_covered, "red")
|
23
|
-
|
24
|
-
final_output
|
25
|
-
|
26
|
-
@total - @covered
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
attr_reader :dotfile
|
32
|
-
|
33
|
-
def collect_coverage
|
34
|
-
openapi_files = ::Swagcov::OpenapiFiles.new(filepaths: dotfile.doc_paths)
|
21
|
+
def collect
|
22
|
+
openapi_files = ::Swagcov::OpenapiFiles.new(filepaths: dotfile.docs_config)
|
23
|
+
rails_version = ::Rails::VERSION::STRING
|
35
24
|
|
36
25
|
@routes.each do |route|
|
37
26
|
path = route.path.spec.to_s.chomp("(.:format)")
|
27
|
+
verb = rails_version > "5" ? route.verb : route.verb.inspect.gsub(%r{[$^/]}, "")
|
38
28
|
|
39
|
-
next if third_party_route?(route, path)
|
29
|
+
next if third_party_route?(route, path, rails_version)
|
40
30
|
|
41
|
-
if dotfile.ignore_path?(path, verb:
|
42
|
-
|
43
|
-
@routes_ignored << { verb: route.verb, path: path, status: "ignored" }
|
31
|
+
if dotfile.ignore_path?(path, verb: verb)
|
32
|
+
update_data(:ignored, verb, path, "ignored")
|
44
33
|
next
|
45
34
|
end
|
46
35
|
|
47
36
|
next if dotfile.only_path_mismatch?(path)
|
48
37
|
|
49
|
-
@
|
38
|
+
@data[:total_count] += 1
|
50
39
|
|
51
|
-
if (response_keys = openapi_files.find_response_keys(path: path, route_verb:
|
52
|
-
|
53
|
-
@routes_covered << { verb: route.verb, path: path, status: response_keys.join(" ") }
|
40
|
+
if (response_keys = openapi_files.find_response_keys(path: path, route_verb: verb))
|
41
|
+
update_data(:covered, verb, path, response_keys.join(" "))
|
54
42
|
else
|
55
|
-
|
43
|
+
update_data(:uncovered, verb, path, "none")
|
56
44
|
end
|
57
45
|
end
|
46
|
+
|
47
|
+
@data
|
58
48
|
end
|
59
49
|
|
60
|
-
|
50
|
+
private
|
51
|
+
|
52
|
+
def third_party_route? route, path, rails_version
|
61
53
|
# https://github.com/rails/rails/blob/48f3c3e201b57a4832314b2c957a3b303e89bfea/actionpack/lib/action_dispatch/routing/inspector.rb#L105-L107
|
62
54
|
# Skips route paths like ["/rails/info/properties", "/rails/info", "/rails/mailers"]
|
63
|
-
route
|
55
|
+
internal_rails_route?(route, rails_version) ||
|
64
56
|
|
65
57
|
# Skips routes like "/sidekiq"
|
66
58
|
route.verb.blank? ||
|
@@ -71,62 +63,17 @@ module Swagcov
|
|
71
63
|
path.include?("/active_storage/") || path.include?("/action_mailbox/")
|
72
64
|
end
|
73
65
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
{ verb: route[:verb], path: route[:path], status: route[:status].send(status_color) }
|
80
|
-
)
|
81
|
-
)
|
66
|
+
def internal_rails_route? route, rails_version
|
67
|
+
if rails_version > "5"
|
68
|
+
route.internal
|
69
|
+
else
|
70
|
+
::ActionDispatch::Routing::RouteWrapper.new(route).internal?
|
82
71
|
end
|
83
72
|
end
|
84
73
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
@routes_ignored.map { |hash| hash[key] } +
|
89
|
-
@routes_not_covered.map { |hash| hash[key] }
|
90
|
-
|
91
|
-
strings.max_by(&:length).size
|
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
|
-
)
|
74
|
+
def update_data key, verb, path, status
|
75
|
+
@data[:"#{key}_count"] += 1
|
76
|
+
@data[key] << { verb: verb, path: path, status: status }
|
130
77
|
end
|
131
78
|
end
|
132
79
|
end
|
data/lib/swagcov/dotfile.rb
CHANGED
@@ -1,58 +1,67 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Swagcov
|
4
|
-
class BadConfigurationError < RuntimeError
|
5
|
-
end
|
6
|
-
|
7
4
|
class Dotfile
|
8
5
|
DEFAULT_CONFIG_FILE_NAME = ".swagcov.yml"
|
6
|
+
TODO_CONFIG_FILE_NAME = ".swagcov_todo.yml"
|
9
7
|
|
10
|
-
def initialize
|
11
|
-
@dotfile = load_yaml(
|
8
|
+
def initialize basename: DEFAULT_CONFIG_FILE_NAME, todo_basename: TODO_CONFIG_FILE_NAME, skip_todo: false
|
9
|
+
@dotfile = load_yaml(basename, required: true)
|
12
10
|
|
13
|
-
raise
|
11
|
+
raise ::Swagcov::Errors::BadConfiguration, "Invalid config file (#{DEFAULT_CONFIG_FILE_NAME})" unless valid?
|
12
|
+
|
13
|
+
@todo_file = load_yaml(todo_basename) unless skip_todo
|
14
|
+
@ignored_regex = path_config_regex(ignored_config)
|
15
|
+
@only_regex = path_config_regex(only_config)
|
14
16
|
end
|
15
17
|
|
16
18
|
def ignore_path? path, verb:
|
17
|
-
|
19
|
+
return false unless @ignored_config
|
20
|
+
|
21
|
+
ignore_all_path_actions = @ignored_regex.match?(path)
|
18
22
|
|
19
|
-
ignored_verbs =
|
23
|
+
ignored_verbs =
|
24
|
+
@ignored_config.select { |config| config[path]&.is_a?(::Array) }.map(&:values).flatten.map(&:downcase)
|
20
25
|
|
21
|
-
return ignore_all_path_actions
|
26
|
+
return ignore_all_path_actions if ignored_verbs.empty?
|
22
27
|
|
23
|
-
ignored_verbs.
|
28
|
+
ignored_verbs.any?(verb.downcase)
|
24
29
|
end
|
25
30
|
|
26
31
|
def only_path_mismatch? path
|
27
|
-
|
32
|
+
@only_config && !@only_regex.match?(path)
|
28
33
|
end
|
29
34
|
|
30
|
-
def
|
31
|
-
dotfile.dig("docs", "paths")
|
35
|
+
def docs_config
|
36
|
+
@docs_config ||= dotfile.dig("docs", "paths")
|
32
37
|
end
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
def load_yaml pathname
|
39
|
-
raise BadConfigurationError, "Missing config file (#{DEFAULT_CONFIG_FILE_NAME})" unless pathname.exist?
|
39
|
+
def ignored_config
|
40
|
+
dotfile_routes = dotfile.dig("routes", "paths", "ignore").to_a
|
41
|
+
todo_routes = todo_file ? todo_file.dig("routes", "paths", "ignore").to_a : []
|
42
|
+
routes = dotfile_routes + todo_routes
|
40
43
|
|
41
|
-
|
42
|
-
rescue Psych::SyntaxError
|
43
|
-
raise BadConfigurationError, "Malinformed config file (#{DEFAULT_CONFIG_FILE_NAME})"
|
44
|
+
@ignored_config ||= routes.empty? ? nil : routes
|
44
45
|
end
|
45
46
|
|
46
|
-
def
|
47
|
-
@
|
47
|
+
def only_config
|
48
|
+
@only_config ||= dotfile.dig("routes", "paths", "only")
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :dotfile, :todo_file
|
54
|
+
|
55
|
+
def load_yaml basename, required: false
|
56
|
+
pathname = ::Swagcov.project_root.join(basename)
|
57
|
+
|
58
|
+
raise ::Swagcov::Errors::BadConfiguration, "Missing config file (#{basename})" if !pathname.exist? && required
|
59
|
+
|
60
|
+
return unless pathname.exist?
|
53
61
|
|
54
|
-
|
55
|
-
|
62
|
+
::YAML.load_file(pathname)
|
63
|
+
rescue ::Psych::SyntaxError
|
64
|
+
raise ::Swagcov::Errors::BadConfiguration, "Malformed config file (#{basename})"
|
56
65
|
end
|
57
66
|
|
58
67
|
def path_config_regex path_config
|
@@ -63,7 +72,7 @@ module Swagcov
|
|
63
72
|
if path.is_a?(::Hash)
|
64
73
|
"^#{path.keys.first}$"
|
65
74
|
else
|
66
|
-
path.
|
75
|
+
path.chr == "^" ? path : "^#{path}$"
|
67
76
|
end
|
68
77
|
end
|
69
78
|
|
@@ -71,7 +80,7 @@ module Swagcov
|
|
71
80
|
end
|
72
81
|
|
73
82
|
def valid?
|
74
|
-
dotfile &&
|
83
|
+
dotfile && docs_config
|
75
84
|
end
|
76
85
|
end
|
77
86
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Swagcov
|
4
|
+
module Formatter
|
5
|
+
class Console
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
def initialize data: ::Swagcov::Coverage.new.collect
|
9
|
+
@data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
routes_output(data[:covered], "green")
|
14
|
+
routes_output(data[:ignored], "yellow")
|
15
|
+
routes_output(data[:uncovered], "red")
|
16
|
+
final_output
|
17
|
+
|
18
|
+
data[:uncovered_count].zero? ? ::Swagcov::STATUS_SUCCESS : ::Swagcov::STATUS_OFFENSES
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def routes_output routes, status_color
|
24
|
+
routes.each do |route|
|
25
|
+
$stdout.puts(
|
26
|
+
format(
|
27
|
+
"%<verb>10s %<path>-#{min_width(:path) + 1}s %<status>s",
|
28
|
+
{ verb: route[:verb], path: route[:path], status: route[:status].send(status_color) }
|
29
|
+
)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def min_width key
|
35
|
+
strings =
|
36
|
+
data[:covered].map { |hash| hash[key] } +
|
37
|
+
data[:ignored].map { |hash| hash[key] } +
|
38
|
+
data[:uncovered].map { |hash| hash[key] }
|
39
|
+
|
40
|
+
strings.max_by(&:length).size
|
41
|
+
end
|
42
|
+
|
43
|
+
def final_output
|
44
|
+
$stdout.puts
|
45
|
+
$stdout.puts(
|
46
|
+
format(
|
47
|
+
"OpenAPI documentation coverage %<percentage>.2f%% (%<covered>d/%<total>d)",
|
48
|
+
{
|
49
|
+
percentage: 100.0 * data[:covered_count] / data[:total_count],
|
50
|
+
covered: data[:covered_count],
|
51
|
+
total: data[:total_count]
|
52
|
+
}
|
53
|
+
)
|
54
|
+
)
|
55
|
+
|
56
|
+
count_output
|
57
|
+
end
|
58
|
+
|
59
|
+
def count_output
|
60
|
+
{
|
61
|
+
ignored: "yellow",
|
62
|
+
total: "blue",
|
63
|
+
covered: "green",
|
64
|
+
uncovered: "red"
|
65
|
+
}.each do |key, color|
|
66
|
+
count = data[:"#{key}_count"]
|
67
|
+
|
68
|
+
$stdout.puts(
|
69
|
+
format(
|
70
|
+
"%<status>s #{key} #{count == 1 ? 'endpoint' : 'endpoints'}",
|
71
|
+
{ status: count.to_s.send(color) }
|
72
|
+
)
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -9,7 +9,7 @@ module Swagcov
|
|
9
9
|
|
10
10
|
def find_response_keys path:, route_verb:
|
11
11
|
# replace :id with {id}
|
12
|
-
regex = Regexp.new("^#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}?$")
|
12
|
+
regex = ::Regexp.new("^#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}?$")
|
13
13
|
|
14
14
|
matching_paths_key = @openapi_paths.keys.grep(regex).first
|
15
15
|
matching_request_method_key = @openapi_paths.dig(matching_paths_key, route_verb.downcase)
|
@@ -20,15 +20,15 @@ module Swagcov
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def load_yamls
|
23
|
-
Dir.glob(@filepaths).reduce({}) do |hash, filepath|
|
23
|
+
::Dir.glob(@filepaths).reduce({}) do |hash, filepath|
|
24
24
|
hash.merge(load_yaml(filepath))
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
def load_yaml filepath
|
29
|
-
YAML.load_file(filepath)["paths"]
|
30
|
-
rescue Psych::SyntaxError
|
31
|
-
raise
|
29
|
+
::YAML.load_file(filepath)["paths"]
|
30
|
+
rescue ::Psych::SyntaxError
|
31
|
+
raise ::Swagcov::Errors::BadConfiguration, "Malformed openapi file (#{filepath})"
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
data/lib/swagcov/version.rb
CHANGED
data/lib/swagcov.rb
CHANGED
@@ -1,16 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
|
5
|
-
if defined?(Rails)
|
6
|
-
require "swagcov/railtie"
|
7
|
-
require "swagcov/dotfile"
|
8
|
-
require "swagcov/coverage"
|
9
|
-
require "swagcov/openapi_files"
|
10
|
-
require "swagcov/install"
|
11
|
-
end
|
3
|
+
require "rails"
|
12
4
|
|
5
|
+
require "swagcov/command/generate_dotfile"
|
6
|
+
require "swagcov/command/generate_todo_file"
|
7
|
+
require "swagcov/command/report_coverage"
|
13
8
|
require "swagcov/core_ext/string"
|
9
|
+
require "swagcov/formatter/console"
|
10
|
+
require "swagcov/coverage"
|
11
|
+
require "swagcov/dotfile"
|
12
|
+
require "swagcov/errors"
|
13
|
+
require "swagcov/openapi_files"
|
14
|
+
require "swagcov/railtie"
|
15
|
+
require "swagcov/version"
|
14
16
|
|
15
17
|
module Swagcov
|
18
|
+
module_function
|
19
|
+
|
20
|
+
STATUS_SUCCESS = 0
|
21
|
+
STATUS_OFFENSES = 1
|
22
|
+
STATUS_ERROR = 2
|
23
|
+
|
24
|
+
def project_root
|
25
|
+
::Rails.root || ::Pathname.new(::FileUtils.pwd)
|
26
|
+
end
|
16
27
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swagcov
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sarah Ridge
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '
|
18
|
+
version: '4.2'
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '
|
25
|
+
version: '4.2'
|
26
26
|
email:
|
27
27
|
- sarahmarie@hey.com
|
28
28
|
executables: []
|
@@ -34,15 +34,20 @@ files:
|
|
34
34
|
- README.md
|
35
35
|
- Rakefile
|
36
36
|
- lib/swagcov.rb
|
37
|
+
- lib/swagcov/command/generate_dotfile.rb
|
38
|
+
- lib/swagcov/command/generate_todo_file.rb
|
39
|
+
- lib/swagcov/command/report_coverage.rb
|
37
40
|
- lib/swagcov/core_ext/string.rb
|
38
41
|
- lib/swagcov/coverage.rb
|
39
42
|
- lib/swagcov/dotfile.rb
|
40
|
-
- lib/swagcov/
|
43
|
+
- lib/swagcov/errors.rb
|
44
|
+
- lib/swagcov/formatter/console.rb
|
41
45
|
- lib/swagcov/openapi_files.rb
|
42
46
|
- lib/swagcov/railtie.rb
|
43
47
|
- lib/swagcov/version.rb
|
48
|
+
- lib/tasks/swagcov.rake
|
49
|
+
- lib/tasks/swagcov/generate_todo.rake
|
44
50
|
- lib/tasks/swagcov/install.rake
|
45
|
-
- lib/tasks/swagcove.rake
|
46
51
|
homepage: https://github.com/smridge/swagcov
|
47
52
|
licenses:
|
48
53
|
- MIT
|
@@ -66,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
71
|
- !ruby/object:Gem::Version
|
67
72
|
version: '0'
|
68
73
|
requirements: []
|
69
|
-
rubygems_version: 3.6.
|
74
|
+
rubygems_version: 3.6.8
|
70
75
|
specification_version: 4
|
71
76
|
summary: OpenAPI documentation coverage for Rails Routes
|
72
77
|
test_files: []
|
data/lib/swagcov/install.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Swagcov
|
4
|
-
class Install
|
5
|
-
attr_reader :dotfile
|
6
|
-
|
7
|
-
def initialize pathname: ::Rails.root.join(::Swagcov::Dotfile::DEFAULT_CONFIG_FILE_NAME).to_s
|
8
|
-
@dotfile = pathname
|
9
|
-
end
|
10
|
-
|
11
|
-
def generate_dotfile
|
12
|
-
if ::File.exist?(dotfile)
|
13
|
-
$stdout.puts "#{dotfile} already exists"
|
14
|
-
return
|
15
|
-
end
|
16
|
-
|
17
|
-
::File.write(
|
18
|
-
dotfile,
|
19
|
-
<<~YAML
|
20
|
-
## Required field:
|
21
|
-
# List your OpenAPI documentation files
|
22
|
-
docs:
|
23
|
-
paths:
|
24
|
-
- swagger.yaml
|
25
|
-
|
26
|
-
## Optional fields:
|
27
|
-
# routes:
|
28
|
-
# paths:
|
29
|
-
# only:
|
30
|
-
# - ^/v2 # only track v2 endpoints
|
31
|
-
# ignore:
|
32
|
-
# - /v2/users # do not track certain endpoints
|
33
|
-
# - /v2/users/:id: # ignore only certain actions (verbs)
|
34
|
-
# - GET
|
35
|
-
YAML
|
36
|
-
)
|
37
|
-
|
38
|
-
$stdout.puts "created #{::Swagcov::Dotfile::DEFAULT_CONFIG_FILE_NAME}"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|