open_api_annotator 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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -0
- data/CHANGELOG.md +4 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/open_api_annotator.rb +41 -0
- data/lib/open_api_annotator/association.rb +3 -0
- data/lib/open_api_annotator/attribute.rb +10 -0
- data/lib/open_api_annotator/components_builder.rb +79 -0
- data/lib/open_api_annotator/config.rb +100 -0
- data/lib/open_api_annotator/configurable.rb +16 -0
- data/lib/open_api_annotator/controller_annotatable.rb +45 -0
- data/lib/open_api_annotator/errors.rb +3 -0
- data/lib/open_api_annotator/field.rb +11 -0
- data/lib/open_api_annotator/format_validator.rb +7 -0
- data/lib/open_api_annotator/nullable_validator.rb +9 -0
- data/lib/open_api_annotator/paths_builder.rb +119 -0
- data/lib/open_api_annotator/serializer_annotatable.rb +100 -0
- data/lib/open_api_annotator/spec_builder.rb +15 -0
- data/lib/open_api_annotator/type_validator.rb +36 -0
- data/lib/open_api_annotator/version.rb +3 -0
- data/lib/tasks/api_spec.rake +12 -0
- data/open_api_annotator.gemspec +37 -0
- metadata +227 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9c25f0d206c6a39ed21f7dd1a15fb886aaad9e6ed05db73aeda5ffa7e852a1ed
|
4
|
+
data.tar.gz: ce7c5a930ec200636792afee7e4545ac13e8ebcc703281dd0da9da431d1857d6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5a7ac62a5ce7fddc89b62b8694f5e0c364a18d963b162f6b7479229d7e5ad1571fda1d64690eef4f1cd833c6bd661e4956788469e6357cf6b2769a3844b26610
|
7
|
+
data.tar.gz: 5ba222eb03b1a1496ef8ce300cf5eaef0b179ee4e5b365348351c71a4eb78da607f8d3b2a1093013e3ad925e09b751b82f7db4b72b2ae62fe2983535aba06472
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.5.1
|
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=921d046db793707b7d0c0d1305970a137fdf98fbc6ceeb6c355c44757040244f
|
4
|
+
sudo: false
|
5
|
+
language: ruby
|
6
|
+
rvm:
|
7
|
+
- 2.4.4
|
8
|
+
- 2.5.1
|
9
|
+
before_install: gem install bundler -v 1.16.1
|
10
|
+
before_script:
|
11
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
12
|
+
- chmod +x ./cc-test-reporter
|
13
|
+
- ./cc-test-reporter before-build
|
14
|
+
after_script:
|
15
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at ngtknt@me.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Kent Nagata
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# OpenApiAnnotator [](https://travis-ci.org/ngtk/open_api_annotator) [](https://codeclimate.com/github/ngtk/open_api_annotator/maintainability) [](https://codeclimate.com/github/ngtk/open_api_annotator/test_coverage)
|
2
|
+
|
3
|
+
OpenApiAnnotator realizes to generate OpenApi spec by annotating to controllers and serializers.
|
4
|
+
If you use ActiveModelSerializer, this is the best to generate OpenAPI spec.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'open_api_annotator'
|
12
|
+
```
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
Annotating controllers and serializers, you can generate OpenAPI spec file from these.
|
17
|
+
Things you have to do are three below:
|
18
|
+
|
19
|
+
1. Configure API meta information
|
20
|
+
1. Annotate controllers
|
21
|
+
1. Annotate serializers
|
22
|
+
|
23
|
+
### 1. Configure API meta information
|
24
|
+
You have to set API meta information like:
|
25
|
+
|
26
|
+
```rb
|
27
|
+
# config/initializers/open_api_annotator.rb
|
28
|
+
OpenApiAnnotator.configure do |config|
|
29
|
+
config.info = OpenApi::Info.new(title: "Book API", version: "1")
|
30
|
+
config.destination_path = Rails.root.join("api_spec.yml")
|
31
|
+
config.path_regexp = /\Aapi\/v1\// # If you want to restrict a path to create
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
### 2. Annotate controller
|
37
|
+
To define an entity of an endpoint, call the method `endpoint` in the previous line of an action method. It takes entity expression as the first arg. Entity expression is a model class or an array that contains only one model class.
|
38
|
+
|
39
|
+
```rb
|
40
|
+
class Api::V1::BooksController
|
41
|
+
endpoint [Book] # 👈It means an array of Book
|
42
|
+
def index
|
43
|
+
books = Book.limit(10)
|
44
|
+
render json: books
|
45
|
+
end
|
46
|
+
|
47
|
+
endpoint Book # 👈Just a Book
|
48
|
+
def show
|
49
|
+
book = Book.find(params[:id])
|
50
|
+
render json: book
|
51
|
+
end
|
52
|
+
|
53
|
+
endpoint Book # 👈Just a Book
|
54
|
+
def update
|
55
|
+
book = Book.find(params[:id])
|
56
|
+
book.update!(book_params)
|
57
|
+
render json: book
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
### 3. Annotate serializer
|
63
|
+
To define an schema in components, set `type`, `format`, `nullable` as each field option.
|
64
|
+
|
65
|
+
```rb
|
66
|
+
class BookSerializer < ApplicationSerializer
|
67
|
+
attribute :title, type: :string, nullable: false
|
68
|
+
attribute :published_at, type: :string, format: :"date-time", nullable: true
|
69
|
+
|
70
|
+
has_many :authors, type: [Author], nullable: false
|
71
|
+
has_one :cover_image, type: CoverImage, nullable: true
|
72
|
+
belongs_to :publisher, type: Publisher, nullable: false
|
73
|
+
end
|
74
|
+
```
|
75
|
+
## Development
|
76
|
+
|
77
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
78
|
+
|
79
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
|
83
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ngtk/open_api_annotator. 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.
|
84
|
+
|
85
|
+
## License
|
86
|
+
|
87
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
88
|
+
|
89
|
+
## Code of Conduct
|
90
|
+
|
91
|
+
Everyone interacting in the OpenApiAnnotator project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ngtk/open_api_annotator/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "open_api_annotator"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/concern"
|
3
|
+
require "active_support/core_ext/class/subclasses"
|
4
|
+
|
5
|
+
require 'rails'
|
6
|
+
|
7
|
+
require 'open_api'
|
8
|
+
|
9
|
+
require 'open_api_annotator/field'
|
10
|
+
require 'open_api_annotator/attribute'
|
11
|
+
require 'open_api_annotator/association'
|
12
|
+
require 'open_api_annotator/version'
|
13
|
+
require 'open_api_annotator/errors'
|
14
|
+
require 'open_api_annotator/controller_annotatable'
|
15
|
+
require 'open_api_annotator/serializer_annotatable'
|
16
|
+
require 'open_api_annotator/type_validator'
|
17
|
+
require 'open_api_annotator/format_validator'
|
18
|
+
require 'open_api_annotator/nullable_validator'
|
19
|
+
require 'open_api_annotator/paths_builder'
|
20
|
+
require 'open_api_annotator/components_builder'
|
21
|
+
require 'open_api_annotator/spec_builder'
|
22
|
+
|
23
|
+
require 'open_api_annotator/config'
|
24
|
+
require 'open_api_annotator/configurable'
|
25
|
+
|
26
|
+
module OpenApiAnnotator
|
27
|
+
include Configurable
|
28
|
+
|
29
|
+
def self.create_spec_yaml
|
30
|
+
info = config.info
|
31
|
+
spec = SpecBuilder.new.build(info: info)
|
32
|
+
yaml = OpenApi::Serializers::YamlSerializer.new.serialize(spec)
|
33
|
+
File.write(config.destination_path, yaml)
|
34
|
+
end
|
35
|
+
|
36
|
+
class Railtie < ::Rails::Railtie
|
37
|
+
rake_tasks do
|
38
|
+
load "tasks/api_spec.rake"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module OpenApiAnnotator
|
2
|
+
class ComponentsBuilder
|
3
|
+
def build
|
4
|
+
components = OpenApi::Components.new(schemas: {})
|
5
|
+
|
6
|
+
serializers = fetch_all_serializers
|
7
|
+
serializers.sort_by!(&:open_api_resource_name)
|
8
|
+
serializers.each do |serializer|
|
9
|
+
schema = build_schema(serializer)
|
10
|
+
next unless schema
|
11
|
+
components.schemas[serializer.open_api_resource_name] = schema
|
12
|
+
puts "Schema component '#{serializer.open_api_resource_name}' has been created."
|
13
|
+
end
|
14
|
+
components
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def build_schema(serializer)
|
20
|
+
schema = OpenApi::Schema.new(type: "object", properties: {})
|
21
|
+
schema.properties.merge!(build_attribute_properties(serializer))
|
22
|
+
schema.properties.merge!(build_has_many_association_properties(serializer))
|
23
|
+
schema.properties.merge!(build_has_one_and_belongs_to_association_properties(serializer))
|
24
|
+
schema
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_attribute_properties(serializer)
|
28
|
+
properties = {}
|
29
|
+
serializer.open_api_attributes.each do |attribute|
|
30
|
+
next unless attribute.valid?
|
31
|
+
properties[attribute.name.to_sym] = OpenApi::Schema.new(
|
32
|
+
type: attribute.type,
|
33
|
+
format: attribute.format,
|
34
|
+
nullable: attribute.nullable,
|
35
|
+
)
|
36
|
+
end
|
37
|
+
properties
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_has_many_association_properties(serializer)
|
41
|
+
properties = {}
|
42
|
+
serializer.open_api_has_many_associations.each do |association|
|
43
|
+
next unless association.valid?
|
44
|
+
content = association.type.first
|
45
|
+
return unless content
|
46
|
+
content_name = content.try(:name) || content.to_s
|
47
|
+
properties[association.name.to_sym] = OpenApi::Schema.new(
|
48
|
+
type: "array",
|
49
|
+
items: OpenApi::Reference.new(ref: "#/components/schemas/#{content_name}"),
|
50
|
+
nullable: association.nullable,
|
51
|
+
)
|
52
|
+
end
|
53
|
+
properties
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_has_one_and_belongs_to_association_properties(serializer)
|
57
|
+
properties = {}
|
58
|
+
associations = serializer.open_api_has_one_associations + serializer.open_api_belongs_to_associations
|
59
|
+
associations.each do |association|
|
60
|
+
next unless association.valid?
|
61
|
+
content_name = association.type.try(:name) || association.type.to_s
|
62
|
+
properties[association.name.to_sym] = OpenApi::Reference.new(ref: "#/components/schemas/#{content_name}")
|
63
|
+
end
|
64
|
+
properties
|
65
|
+
end
|
66
|
+
|
67
|
+
def fetch_all_serializers
|
68
|
+
require_all_serializers!
|
69
|
+
|
70
|
+
OpenApiAnnotator.config.application_serializer_class.descendants
|
71
|
+
end
|
72
|
+
|
73
|
+
def require_all_serializers!
|
74
|
+
all_serializer_features = Dir["#{Rails.root}/app/serializers/**/*_serializer.rb"]
|
75
|
+
unloaded_features = all_serializer_features - $LOADED_FEATURES
|
76
|
+
unloaded_features.each { |f| require f }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module OpenApiAnnotator
|
2
|
+
class Config < Struct.new(
|
3
|
+
:info,
|
4
|
+
:destination_path,
|
5
|
+
:path_regexp,
|
6
|
+
:application_controller_class_name,
|
7
|
+
:application_serializer_class_name,
|
8
|
+
)
|
9
|
+
def application_serializer_class
|
10
|
+
if application_serializer_class_name
|
11
|
+
application_serializer_class_name.constantize
|
12
|
+
else
|
13
|
+
unless defined?(ApplicationSerializer)
|
14
|
+
raise <<~EOL
|
15
|
+
Expected to define ApplicationSerializer or set custom class like:
|
16
|
+
|
17
|
+
```
|
18
|
+
OpenApiAnnotator.configure do |config|
|
19
|
+
config.application_serializer_class = BaseSerializer
|
20
|
+
end
|
21
|
+
```
|
22
|
+
EOL
|
23
|
+
end
|
24
|
+
ApplicationSerializer
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def application_controller_class
|
29
|
+
if application_controller_class_name
|
30
|
+
application_controller_class_name.constantize
|
31
|
+
else
|
32
|
+
unless defined?(ApplicationController)
|
33
|
+
raise <<~EOL
|
34
|
+
Expected to define ApplicationController or set custom class like:
|
35
|
+
|
36
|
+
```
|
37
|
+
OpenApiAnnotator.configure do |config|
|
38
|
+
config.application_controller_class = BaseSerializer
|
39
|
+
end
|
40
|
+
```
|
41
|
+
EOL
|
42
|
+
end
|
43
|
+
ApplicationController
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate!
|
48
|
+
validate_info!
|
49
|
+
validate_destination_path!
|
50
|
+
validate_path_regexp!
|
51
|
+
validate_application_controller_class_name!
|
52
|
+
validate_application_serializer_class_name!
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_info!
|
56
|
+
unless info
|
57
|
+
raise InvalidError, <<~EOL
|
58
|
+
You have to set `OpenApi::Info` to `config.info` like:
|
59
|
+
|
60
|
+
```
|
61
|
+
OpenApiAnnotator.configure do |config|
|
62
|
+
config.info = OpenApi::Info.new(title: "Book API", version: "1")
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
You can see the detail of `OpenApi::Info` in
|
67
|
+
https://www.rubydoc.info/gems/open_api/OpenApi/Info
|
68
|
+
EOL
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_destination_path!
|
73
|
+
unless destination_path
|
74
|
+
raise InvalidError, <<~EOL
|
75
|
+
You have to set `config.destination_path` like:
|
76
|
+
|
77
|
+
```
|
78
|
+
OpenApiAnnotator.configure do |config|
|
79
|
+
config.destination_path = Rails.root.join("api_spec.yml")
|
80
|
+
end
|
81
|
+
```
|
82
|
+
EOL
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate_path_regexp!
|
87
|
+
# Do nothing
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_application_serializer_class_name!
|
91
|
+
# Do nothing
|
92
|
+
end
|
93
|
+
|
94
|
+
def validate_application_controller_class_name!
|
95
|
+
# Do nothing
|
96
|
+
end
|
97
|
+
|
98
|
+
class InvalidError < StandardError; end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module OpenApiAnnotator
|
2
|
+
module ControllerAnnotatable
|
3
|
+
def type_hash
|
4
|
+
@type_hash ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate_open_api_type!(type)
|
8
|
+
@open_api_type_validator ||= TypeValidator.new
|
9
|
+
@open_api_type_validator.validate!(type)
|
10
|
+
end
|
11
|
+
|
12
|
+
def endpoint(type)
|
13
|
+
validate_open_api_type!(type)
|
14
|
+
@last_type = type
|
15
|
+
|
16
|
+
rescue ValidationError => e
|
17
|
+
raise TypeError, <<~EOL
|
18
|
+
#{e.message}
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
- `[Project]`: means collection of Project
|
22
|
+
- `Project`: means single resouce Project
|
23
|
+
|
24
|
+
In your controller:
|
25
|
+
|
26
|
+
endpoint [Project]
|
27
|
+
def index
|
28
|
+
# ... some code
|
29
|
+
end
|
30
|
+
EOL
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_added(name)
|
34
|
+
super
|
35
|
+
return unless @last_type
|
36
|
+
|
37
|
+
type = @last_type
|
38
|
+
@last_type = nil
|
39
|
+
|
40
|
+
return if private_method_defined?(name)
|
41
|
+
|
42
|
+
type_hash[name] = type
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module OpenApiAnnotator
|
2
|
+
class PathsBuilder
|
3
|
+
def build
|
4
|
+
paths = OpenApi::Paths.new
|
5
|
+
routes = RoutesFinder.new.find_all
|
6
|
+
if OpenApiAnnotator.config.path_regexp
|
7
|
+
routes.select! { |route| route.path.match(OpenApiAnnotator.config.path_regexp) }
|
8
|
+
end
|
9
|
+
routes.group_by(&:path).each do |path_name, routes|
|
10
|
+
paths[path_name] = build_path_item(routes)
|
11
|
+
puts "Path '#{path_name}' has been created."
|
12
|
+
end
|
13
|
+
|
14
|
+
paths
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def build_path_item(routes)
|
20
|
+
path_item = OpenApi::PathItem.new
|
21
|
+
routes.each do |route|
|
22
|
+
media_type = resolve_media_type(route.controller_name, route.action_name)
|
23
|
+
description = build_description(route.controller_name, route.action_name)
|
24
|
+
next unless media_type
|
25
|
+
response = OpenApi::Response.new(
|
26
|
+
description: description,
|
27
|
+
content: {
|
28
|
+
"application/json" => media_type,
|
29
|
+
}
|
30
|
+
)
|
31
|
+
operation = OpenApi::Operation.new(responses: OpenApi::Responses.new)
|
32
|
+
operation.responses["200"] = response
|
33
|
+
path_item.operations[route.http_verb.underscore] = operation
|
34
|
+
end
|
35
|
+
path_item
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_description(controller_name, action_name)
|
39
|
+
type = resolve_type(controller_name, action_name)
|
40
|
+
return unless type
|
41
|
+
|
42
|
+
case type
|
43
|
+
when Array
|
44
|
+
"Returns array of #{type.first.name}"
|
45
|
+
when Class
|
46
|
+
"Returns #{type.name}"
|
47
|
+
else
|
48
|
+
raise "not supported class #{type.class}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def resolve_media_type(controller_name, action_name)
|
53
|
+
type = resolve_type(controller_name, action_name)
|
54
|
+
return unless type
|
55
|
+
|
56
|
+
case type
|
57
|
+
when Array
|
58
|
+
content_class = type.first
|
59
|
+
reference = OpenApi::Reference.new(ref: "#/components/schemas/#{content_class.name}")
|
60
|
+
schema = OpenApi::Schema.new(type: "array", items: reference)
|
61
|
+
OpenApi::MediaType.new(schema: schema)
|
62
|
+
when Class
|
63
|
+
reference = OpenApi::Reference.new(ref: "#/components/schemas/#{type.name}")
|
64
|
+
OpenApi::MediaType.new(schema: reference)
|
65
|
+
else
|
66
|
+
raise "not supported class #{type.class}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def resolve_type(controller_name, action_name)
|
71
|
+
controller_class_name = "#{controller_name}_controller".classify
|
72
|
+
begin
|
73
|
+
controller_class = controller_class_name.constantize
|
74
|
+
rescue NameError => e
|
75
|
+
return
|
76
|
+
end
|
77
|
+
return unless controller_class < OpenApiAnnotator.config.application_controller_class
|
78
|
+
|
79
|
+
controller_class.type_hash[action_name.to_sym]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Route = Struct.new(:http_verb, :path, :controller_name, :action_name) do
|
84
|
+
def initialize(http_verb:, path:, controller_name:, action_name:)
|
85
|
+
self.http_verb = http_verb
|
86
|
+
self.path = path
|
87
|
+
self.controller_name = controller_name
|
88
|
+
self.action_name = action_name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class RoutesFinder
|
93
|
+
def find_all
|
94
|
+
@routes ||= Rails.application.routes.routes.routes.map do |route|
|
95
|
+
path = PathResolver.new.resolve(route.path.ast)
|
96
|
+
controller = route.requirements[:controller]
|
97
|
+
action = route.requirements[:action]
|
98
|
+
Route.new(http_verb: route.verb, path: path, controller_name: controller, action_name: action)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class PathResolver
|
104
|
+
def resolve(ast)
|
105
|
+
res = ""
|
106
|
+
if ast.type == :CAT
|
107
|
+
left = ast.left
|
108
|
+
res +=
|
109
|
+
if left.type == :SYMBOL
|
110
|
+
"{#{left.name}}"
|
111
|
+
else
|
112
|
+
left.to_s
|
113
|
+
end
|
114
|
+
res += resolve(ast.right)
|
115
|
+
end
|
116
|
+
res
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module OpenApiAnnotator
|
2
|
+
module SerializerAnnotatable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def skip_open_api_validation!
|
7
|
+
@open_api_validation_skipped = true
|
8
|
+
end
|
9
|
+
|
10
|
+
def attribute(attr, options = {}, &block)
|
11
|
+
validate_open_api_options(attr, options)
|
12
|
+
super(attr, options, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def has_many(name, options = {}, &block)
|
16
|
+
validate_open_api_options(name, options)
|
17
|
+
super(name, options, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_one(name, options = {}, &block)
|
21
|
+
validate_open_api_options(name, options)
|
22
|
+
super(name, options, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def belongs_to(name, options = {}, &block)
|
26
|
+
validate_open_api_options(name, options)
|
27
|
+
super(name, options, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_open_api_options(field, options)
|
31
|
+
return if @open_api_validation_skipped
|
32
|
+
|
33
|
+
validate_open_api_type!(options[:type])
|
34
|
+
validate_open_api_format!(options[:format])
|
35
|
+
validate_open_api_nullable!(options[:nullable])
|
36
|
+
rescue ValidationError => e
|
37
|
+
Rails.logger.warn(e.message)
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_open_api_type!(type)
|
41
|
+
@open_api_type_validator ||= TypeValidator.new
|
42
|
+
@open_api_type_validator.validate!(type)
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_open_api_nullable!(type)
|
46
|
+
@open_api_nullable_validator ||= NullableValidator.new
|
47
|
+
@open_api_nullable_validator.validate!(type)
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_open_api_format!(format)
|
51
|
+
@open_api_format_validator ||= FormatValidator.new
|
52
|
+
@open_api_format_validator.validate!(format)
|
53
|
+
end
|
54
|
+
|
55
|
+
def open_api_has_many_associations
|
56
|
+
_reflections.values.select { |reflection|
|
57
|
+
reflection.is_a?(ActiveModel::Serializer::HasManyReflection)
|
58
|
+
}.map do |reflection|
|
59
|
+
serializer_class = reflection.options[:serializer]
|
60
|
+
type = serializer_class ? [serializer_class.open_api_resource_name] : reflection.options[:type]
|
61
|
+
Association.new(reflection.name.to_sym, type, reflection.options[:nullable])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def open_api_has_one_associations
|
66
|
+
_reflections.values.select { |reflection|
|
67
|
+
reflection.is_a?(ActiveModel::Serializer::HasOneReflection)
|
68
|
+
}.map do |reflection|
|
69
|
+
serializer_class = reflection.options[:serializer]
|
70
|
+
type = serializer_class ? [serializer_class.open_api_resource_name] : reflection.options[:type]
|
71
|
+
Association.new(reflection.name.to_sym, type, reflection.options[:nullable])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def open_api_belongs_to_associations
|
76
|
+
_reflections.values.select { |reflection|
|
77
|
+
reflection.is_a?(ActiveModel::Serializer::BelongsToReflection)
|
78
|
+
}.map do |reflection|
|
79
|
+
serializer_class = reflection.options[:serializer]
|
80
|
+
type = serializer_class ? [serializer_class.open_api_resource_name] : reflection.options[:type]
|
81
|
+
Association.new(reflection.name.to_sym, type, reflection.options[:nullable])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def open_api_attributes
|
86
|
+
_attributes_data.values.reject { |attribute|
|
87
|
+
attribute.name.to_s.start_with?("_")
|
88
|
+
}.map { |attribute|
|
89
|
+
serializer_class = attribute.options[:serializer]
|
90
|
+
type = serializer_class ? [serializer_class.open_api_resource_name] : attribute.options[:type]
|
91
|
+
Attribute.new(attribute.name.to_sym, type, attribute.options[:format], attribute.options[:nullable])
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def open_api_resource_name
|
96
|
+
name.remove(/Serializer\z/)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module OpenApiAnnotator
|
2
|
+
class SpecBuilder
|
3
|
+
def build(info:)
|
4
|
+
paths = PathsBuilder.new.build
|
5
|
+
components = ComponentsBuilder.new.build
|
6
|
+
|
7
|
+
OpenApi::Specification.new(
|
8
|
+
openapi: "3.0.1",
|
9
|
+
info: info,
|
10
|
+
paths: paths,
|
11
|
+
components: components,
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module OpenApiAnnotator
|
2
|
+
class TypeValidator
|
3
|
+
def validate!(type)
|
4
|
+
if type.is_a?(Array)
|
5
|
+
validate_as_collection_resource!(type)
|
6
|
+
else
|
7
|
+
validate_as_single_resource!(type)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def validate_as_collection_resource!(type)
|
14
|
+
unless type.size == 1
|
15
|
+
raise ValidationError, "type array should have one element, but it has #{type.size}."
|
16
|
+
end
|
17
|
+
|
18
|
+
validate_as_single_resource!(type[0])
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_as_single_resource!(type)
|
22
|
+
case type
|
23
|
+
when Symbol
|
24
|
+
unless type.in?(OpenApi::DataTypes.all_types)
|
25
|
+
raise ValidationError, "type should be a symbol of: #{OpenApi::DataTypes.all_types.join(", ")}, but got #{type}."
|
26
|
+
end
|
27
|
+
when Class
|
28
|
+
# pass
|
29
|
+
when NilClass
|
30
|
+
raise ValidationError, "type should not be nil."
|
31
|
+
else
|
32
|
+
raise ValidationError, "type is unexpected class #{type.class}."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "open_api_annotator/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "open_api_annotator"
|
8
|
+
spec.version = OpenApiAnnotator::VERSION
|
9
|
+
spec.authors = ["Kent Nagata"]
|
10
|
+
spec.email = ["ngtknt@me.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{OpenApi spec generation by bottom-up.}
|
13
|
+
spec.description = %q{OpenApiAnnotator realizes to generate OpenApi spec by annotating to Controller and ActiveModelSerializer.}
|
14
|
+
spec.homepage = "https://github.com/ngtk/open_api_annotator"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "open_api", ">= 0.3.3"
|
25
|
+
spec.add_dependency "active_model_serializers", "~> 0.10.0"
|
26
|
+
|
27
|
+
rails_versions = ['>= 4.1', '< 6']
|
28
|
+
spec.add_dependency "actionpack", rails_versions
|
29
|
+
spec.add_dependency "railties", rails_versions
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
32
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
+
spec.add_development_dependency "pry"
|
35
|
+
spec.add_development_dependency "simplecov"
|
36
|
+
spec.add_development_dependency "bump"
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: open_api_annotator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kent Nagata
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: open_api
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.3.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: active_model_serializers
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.10.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.10.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: actionpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.1'
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '6'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '4.1'
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '6'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: railties
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '4.1'
|
68
|
+
- - "<"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '6'
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '4.1'
|
78
|
+
- - "<"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '6'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: bundler
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '1.16'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '1.16'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rake
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '10.0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '10.0'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: rspec
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '3.0'
|
116
|
+
type: :development
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '3.0'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: pry
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: simplecov
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
type: :development
|
145
|
+
prerelease: false
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
- !ruby/object:Gem::Dependency
|
152
|
+
name: bump
|
153
|
+
requirement: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
type: :development
|
159
|
+
prerelease: false
|
160
|
+
version_requirements: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
description: OpenApiAnnotator realizes to generate OpenApi spec by annotating to Controller
|
166
|
+
and ActiveModelSerializer.
|
167
|
+
email:
|
168
|
+
- ngtknt@me.com
|
169
|
+
executables: []
|
170
|
+
extensions: []
|
171
|
+
extra_rdoc_files: []
|
172
|
+
files:
|
173
|
+
- ".gitignore"
|
174
|
+
- ".rspec"
|
175
|
+
- ".ruby-version"
|
176
|
+
- ".travis.yml"
|
177
|
+
- CHANGELOG.md
|
178
|
+
- CODE_OF_CONDUCT.md
|
179
|
+
- Gemfile
|
180
|
+
- LICENSE.txt
|
181
|
+
- README.md
|
182
|
+
- Rakefile
|
183
|
+
- bin/console
|
184
|
+
- bin/setup
|
185
|
+
- lib/open_api_annotator.rb
|
186
|
+
- lib/open_api_annotator/association.rb
|
187
|
+
- lib/open_api_annotator/attribute.rb
|
188
|
+
- lib/open_api_annotator/components_builder.rb
|
189
|
+
- lib/open_api_annotator/config.rb
|
190
|
+
- lib/open_api_annotator/configurable.rb
|
191
|
+
- lib/open_api_annotator/controller_annotatable.rb
|
192
|
+
- lib/open_api_annotator/errors.rb
|
193
|
+
- lib/open_api_annotator/field.rb
|
194
|
+
- lib/open_api_annotator/format_validator.rb
|
195
|
+
- lib/open_api_annotator/nullable_validator.rb
|
196
|
+
- lib/open_api_annotator/paths_builder.rb
|
197
|
+
- lib/open_api_annotator/serializer_annotatable.rb
|
198
|
+
- lib/open_api_annotator/spec_builder.rb
|
199
|
+
- lib/open_api_annotator/type_validator.rb
|
200
|
+
- lib/open_api_annotator/version.rb
|
201
|
+
- lib/tasks/api_spec.rake
|
202
|
+
- open_api_annotator.gemspec
|
203
|
+
homepage: https://github.com/ngtk/open_api_annotator
|
204
|
+
licenses:
|
205
|
+
- MIT
|
206
|
+
metadata: {}
|
207
|
+
post_install_message:
|
208
|
+
rdoc_options: []
|
209
|
+
require_paths:
|
210
|
+
- lib
|
211
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
|
+
requirements:
|
218
|
+
- - ">="
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '0'
|
221
|
+
requirements: []
|
222
|
+
rubyforge_project:
|
223
|
+
rubygems_version: 2.7.6
|
224
|
+
signing_key:
|
225
|
+
specification_version: 4
|
226
|
+
summary: OpenApi spec generation by bottom-up.
|
227
|
+
test_files: []
|