open_api-loader 0.0.1
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/.codeclimate.yml +15 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +28 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +99 -0
- data/Rakefile +10 -0
- data/bin/console +6 -0
- data/bin/setup +6 -0
- data/lib/open_api-loader.rb +1 -0
- data/lib/open_api/loader.rb +44 -0
- data/lib/open_api/loader/collector.rb +61 -0
- data/lib/open_api/loader/denormalizer.rb +27 -0
- data/lib/open_api/loader/denormalizer/parameters.rb +46 -0
- data/lib/open_api/loader/denormalizer/security.rb +35 -0
- data/lib/open_api/loader/denormalizer/servers.rb +36 -0
- data/lib/open_api/loader/denormalizer/variables.rb +54 -0
- data/lib/open_api/loader/reader.rb +47 -0
- data/lib/open_api/loader/ref.rb +85 -0
- data/lib/open_api/loader/translator.rb +50 -0
- data/lib/open_api/loader/translator/clean_definitions.rb +16 -0
- data/lib/open_api/loader/translator/convert_bodies.rb +77 -0
- data/lib/open_api/loader/translator/convert_forms.rb +71 -0
- data/lib/open_api/loader/translator/convert_parameters.rb +135 -0
- data/lib/open_api/loader/translator/convert_responses.rb +63 -0
- data/lib/open_api/loader/translator/convert_security_schemes.rb +49 -0
- data/lib/open_api/loader/translator/convert_servers.rb +46 -0
- data/lib/open_api/loader/translator/convert_version.rb +12 -0
- data/lib/open_api/loader/translator/denormalize_consumes.rb +44 -0
- data/lib/open_api/loader/translator/denormalize_parameters.rb +55 -0
- data/lib/open_api/loader/translator/denormalize_produces.rb +53 -0
- data/open_api-loader.gemspec +25 -0
- data/spec/fixtures/oas2/collected.yaml +1012 -0
- data/spec/fixtures/oas2/denormalized.yaml +1462 -0
- data/spec/fixtures/oas2/loaded.yaml +564 -0
- data/spec/fixtures/oas2/models.yaml +118 -0
- data/spec/fixtures/oas2/source.json +1 -0
- data/spec/fixtures/oas2/source.yaml +569 -0
- data/spec/fixtures/oas2/translated.yaml +1396 -0
- data/spec/fixtures/oas3/collected.yaml +233 -0
- data/spec/fixtures/oas3/denormalized.yaml +233 -0
- data/spec/fixtures/oas3/source.json +1 -0
- data/spec/fixtures/oas3/source.yaml +217 -0
- data/spec/loader_spec.rb +53 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/fixture_helpers.rb +18 -0
- data/spec/support/path_helpers.rb +27 -0
- data/spec/unit/collector_spec.rb +31 -0
- data/spec/unit/denormalizer_spec.rb +17 -0
- data/spec/unit/reader_spec.rb +53 -0
- data/spec/unit/ref_spec.rb +107 -0
- data/spec/unit/translator_spec.rb +15 -0
- metadata +232 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bfec60cf6253d6d98d6e98dfb40314dbfb3922cc
|
4
|
+
data.tar.gz: 95607528b55ac357d5aad01e8d56b842a938f843
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 98e1d24f7bd622ef43a98adef44fc56092de155462f4e7e1a1df96a9672f761c541435794b49a11850a4c8a82f72375efeb643e340f3a29895955131732aec83
|
7
|
+
data.tar.gz: a5c8e547e3f5e77102f84a0b893715f57d8ea1a847ef7c16fefc5bfff7ddff5db1491b07a991a65887b4a27f4ba2155fe98ddf6ec9e1df10da1749d44a5ec647
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
AllCops:
|
3
|
+
DisplayCopNames: true
|
4
|
+
DisplayStyleGuide: true
|
5
|
+
StyleGuideCopsOnly: true
|
6
|
+
TargetRubyVersion: 2.5
|
7
|
+
|
8
|
+
Naming/FileName:
|
9
|
+
Exclude:
|
10
|
+
- "lib/open_api-loader.rb"
|
11
|
+
|
12
|
+
Style/Alias:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Style/CaseEquality:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/ClassAndModuleChildren:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/DateTime:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/DoubleNegation:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/StringLiterals:
|
28
|
+
EnforcedStyle: double_quotes
|
data/.travis.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
language: ruby
|
3
|
+
sudo: false
|
4
|
+
cache: bundler
|
5
|
+
bundler_args: --without benchmarks tools
|
6
|
+
script: bundle exec rake
|
7
|
+
rvm:
|
8
|
+
- 2.3.0
|
9
|
+
- 2.4.0
|
10
|
+
- 2.5.0
|
11
|
+
- ruby-head
|
12
|
+
- jruby-9.1.0.0
|
13
|
+
- rbx-3
|
14
|
+
env:
|
15
|
+
global:
|
16
|
+
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
17
|
+
matrix:
|
18
|
+
allow_failures:
|
19
|
+
- rvm: rbx-3
|
20
|
+
- rvm: ruby-head
|
21
|
+
- rvm: jruby-head
|
22
|
+
include:
|
23
|
+
- rvm: jruby-head
|
24
|
+
before_install: gem install bundler --no-ri --no-rdoc
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
|
+
|
7
|
+
## [0.0.1] - [Unreleased]
|
8
|
+
This is a first public release
|
9
|
+
|
10
|
+
[Unreleased]: https://github.com/nepalez/open_api-loader
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018, Andrew Kozin (nepalez), andrew.kozin@gmail.com
|
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,99 @@
|
|
1
|
+
# OpenAPI::Loader
|
2
|
+
|
3
|
+
Loads [OAS2][oas2] or [OAS3][oas3] scheme from YAML/JSON file(s), and converts it to OAS3 standard.
|
4
|
+
|
5
|
+
<a href="https://evilmartians.com/">
|
6
|
+
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
7
|
+
|
8
|
+
[![Gem Version][gem-badger]][gem]
|
9
|
+
[![Build Status][travis-badger]][travis]
|
10
|
+
[![Inline docs][inch-badger]][inch]
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'open_api-loader'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
```shell
|
23
|
+
$ bundle
|
24
|
+
```
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
```shell
|
29
|
+
$ gem install open_api-loader
|
30
|
+
```
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
require "open_api/loader"
|
36
|
+
|
37
|
+
OpenAPI::Loader.call "path_to/source.yaml"
|
38
|
+
# => { "openapi" => "3.0.0", ... }
|
39
|
+
```
|
40
|
+
|
41
|
+
It takes a filename of the specification (it should be in YAML or JSON), and returns a nested hash.
|
42
|
+
|
43
|
+
The loader transforms the source in several ways:
|
44
|
+
|
45
|
+
- reads the data,
|
46
|
+
- collects all the [reference objects][ref] both local and remote (like `./models.yaml#/Pet` or `https://example.com/models#/Pet`), and includes them into the specification,
|
47
|
+
- converts the specification from [OAS2][oas2] into [OAS3][oas3] standard,
|
48
|
+
- removes the [components][components] because all links to its definitions are dereferenced,
|
49
|
+
- denormalizes [servers][servers], [security][security], and [parameters][parameters] by moving shared definitions from both [root][root] and [path items][paths] right into the corresponding [operations][operations],
|
50
|
+
- substitutes [server variables][server variables] into urls to provide "flat" servers list.
|
51
|
+
|
52
|
+
You can skip the last 3 steps using option:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
OpenAPI::Loader.call "path_to/source.yaml", denormalize: false
|
56
|
+
```
|
57
|
+
|
58
|
+
## Development and Contributing
|
59
|
+
|
60
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nepalez/open_api-loader.
|
61
|
+
|
62
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
63
|
+
|
64
|
+
Before adding a PR, please run `rake` to run tests, style and documentation linters. You can also run `bin/console` for an interactive prompt [pry][pry] that will allow you to experiment.
|
65
|
+
|
66
|
+
## TODO
|
67
|
+
|
68
|
+
1. Check whether a [reference objects][ref] contain cycles
|
69
|
+
2. Add script to run the loader from shell and save results to YAML/JSON file
|
70
|
+
|
71
|
+
```shell
|
72
|
+
$ open_api load -s source.yaml -o output.yaml -d
|
73
|
+
```
|
74
|
+
|
75
|
+
## License
|
76
|
+
|
77
|
+
The gem is available as open source under the terms of the [MIT License][license].
|
78
|
+
|
79
|
+
[codeclimate-badger]: https://img.shields.io/codeclimate/github/nepalez/open_api-loader.svg?style=flat
|
80
|
+
[codeclimate]: https://codeclimate.com/github/nepalez/open_api-loader
|
81
|
+
[components]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#componentsObject
|
82
|
+
[gem-badger]: https://img.shields.io/gem/v/open_api-loader.svg?style=flat
|
83
|
+
[gem]: https://rubygems.org/gems/open_api-loader
|
84
|
+
[inch-badger]: http://inch-ci.org/github/nepalez/open_api-loader.svg
|
85
|
+
[inch]: https://inch-ci.org/github/nepalez/open_api-loader
|
86
|
+
[license]: https://opensource.org/licenses/MIT
|
87
|
+
[oas2]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
|
88
|
+
[oas3]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
89
|
+
[operations]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#operationObject
|
90
|
+
[parameters]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#parameterObject
|
91
|
+
[paths]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject
|
92
|
+
[pry]: https://github.com/pry/pry
|
93
|
+
[ref]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#referenceObject
|
94
|
+
[root]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#oasObject
|
95
|
+
[server variables]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#serverVariableObject
|
96
|
+
[servers]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#serverObject
|
97
|
+
[security]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#securityRequirementObject
|
98
|
+
[travis-badger]: https://img.shields.io/travis/nepalez/open_api-loader/master.svg?style=flat
|
99
|
+
[travis]: https://travis-ci.org/nepalez/open_api-loader
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "open_api/loader"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "constructor_shortcut"
|
2
|
+
require "dry-initializer"
|
3
|
+
require "json"
|
4
|
+
require "yaml"
|
5
|
+
require "uri"
|
6
|
+
require "delegate"
|
7
|
+
|
8
|
+
#
|
9
|
+
# Namespace for gems dealing with OAS specifications
|
10
|
+
#
|
11
|
+
module OpenAPI
|
12
|
+
#
|
13
|
+
# Loads OAS specification from a file and converts it to OAS3 format
|
14
|
+
#
|
15
|
+
# @see OAS2
|
16
|
+
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
|
17
|
+
# @see OAS3
|
18
|
+
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
19
|
+
#
|
20
|
+
module Loader
|
21
|
+
module_function
|
22
|
+
|
23
|
+
require_relative "loader/ref"
|
24
|
+
require_relative "loader/reader"
|
25
|
+
require_relative "loader/collector"
|
26
|
+
require_relative "loader/translator"
|
27
|
+
require_relative "loader/denormalizer"
|
28
|
+
|
29
|
+
#
|
30
|
+
# Loads the specification from given file
|
31
|
+
#
|
32
|
+
# @param [String] filename The name of file containing a specification
|
33
|
+
# @option [Boolean] :denormalize Whether to denormalize specification
|
34
|
+
# @return [Hash] the specification
|
35
|
+
#
|
36
|
+
def call(filename, denormalize: true)
|
37
|
+
normalized = [Collector, Translator].inject(filename) do |output, item|
|
38
|
+
item.call(output)
|
39
|
+
end
|
40
|
+
|
41
|
+
denormalize ? Denormalizer.call(normalized) : normalized
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module OpenAPI::Loader
|
2
|
+
#
|
3
|
+
# Collects the schema from given [#source] file
|
4
|
+
# - loads the schema (and recursively loads and caches all its "$ref" files)
|
5
|
+
# - hashifies the schema to provide nested hash with stringified keys
|
6
|
+
# - collects the schema from the cached sources
|
7
|
+
#
|
8
|
+
# Notice: The service is one-off because its instances cache variables:
|
9
|
+
# `@uri` and `@path` (to provide absolute paths for `$ref`-s)
|
10
|
+
# `@refs` (to minimize the number of remote file loading)
|
11
|
+
#
|
12
|
+
# TODO: For now it doesn't check that the graph of "$ref"-s has no cycles!
|
13
|
+
#
|
14
|
+
# @private
|
15
|
+
#
|
16
|
+
class Collector
|
17
|
+
extend Dry::Initializer
|
18
|
+
extend ConstructorShortcut[:call] # class-level .call
|
19
|
+
|
20
|
+
param :source, proc(&:to_s)
|
21
|
+
option :uri, default: -> { URI(source) if URI(source).absolute? }
|
22
|
+
option :path, default: -> { Pathname(source) unless uri }
|
23
|
+
|
24
|
+
def call
|
25
|
+
build refs[""]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def refs
|
31
|
+
@refs ||= { "" => Reader.call(source) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def build(data)
|
35
|
+
case data = deref(data)
|
36
|
+
when Hash then data.each_with_object({}) { |(k, v), o| o[k] = build(v) }
|
37
|
+
when Array then data.map { |item| build(item) }
|
38
|
+
else data
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def deref(data)
|
43
|
+
ref = data["$ref"] if data.is_a? Hash
|
44
|
+
return data unless ref
|
45
|
+
|
46
|
+
pointer = Ref.new(ref)
|
47
|
+
source = remote(pointer.uri.to_s)
|
48
|
+
pointer.fetch_from(source)
|
49
|
+
end
|
50
|
+
|
51
|
+
def remote(value)
|
52
|
+
refs[value] ||= Collector.call absolute_path(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
def absolute_path(value)
|
56
|
+
return value if URI(value).absolute?
|
57
|
+
return value if Pathname(value).absolute?
|
58
|
+
uri&.merge(value) || path.dirname.join(value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module OpenAPI::Loader
|
2
|
+
#
|
3
|
+
# Denormalizes OAS3 `parameters`, `servers`, and `security`
|
4
|
+
# by moving them from OpenAPI root and Path objects
|
5
|
+
# right into the corresponding Operation objects.
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
#
|
9
|
+
class Denormalizer
|
10
|
+
require_relative "denormalizer/parameters"
|
11
|
+
require_relative "denormalizer/security"
|
12
|
+
require_relative "denormalizer/servers"
|
13
|
+
require_relative "denormalizer/variables"
|
14
|
+
|
15
|
+
extend Dry::Initializer
|
16
|
+
extend ConstructorShortcut[:call] # class-level .call
|
17
|
+
|
18
|
+
param :source
|
19
|
+
|
20
|
+
def call
|
21
|
+
WRAPPERS.each { |wrapper| wrapper.new(source).call }
|
22
|
+
source
|
23
|
+
end
|
24
|
+
|
25
|
+
WRAPPERS = [Parameters, Security, Servers, Variables].freeze
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class OpenAPI::Loader::Denormalizer
|
2
|
+
#
|
3
|
+
# Denormalizes all the 'parameters' definitions
|
4
|
+
# by moving them from the root OpenAPI object and path objects
|
5
|
+
# right into the corresponding operation objects.
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
#
|
9
|
+
class Parameters < SimpleDelegator
|
10
|
+
def call
|
11
|
+
root_params = extract_from(self)
|
12
|
+
paths.each do |path|
|
13
|
+
path_params = extract_from(path, root_params)
|
14
|
+
operations(path).each do |operation|
|
15
|
+
parameters = extract_from(operation, path_params)
|
16
|
+
operation["parameters"] = parameters if parameters.any?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def paths
|
24
|
+
Enumerator.new do |yielder|
|
25
|
+
fetch("paths", {}).each_value do |path|
|
26
|
+
yielder << path if path.is_a? Hash
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def operations(path)
|
32
|
+
Enumerator.new do |yielder|
|
33
|
+
path.each_value { |item| yielder << item if item.is_a? Hash }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def extract_from(data, default = [])
|
38
|
+
custom = Array(data.delete("parameters")).select do |item|
|
39
|
+
item.is_a?(Hash) && item["name"]
|
40
|
+
end
|
41
|
+
|
42
|
+
custom_names = custom.map { |item| item["name"] }
|
43
|
+
default.reject { |item| custom_names.include? item["name"] } + custom
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|