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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +28 -0
  6. data/.travis.yml +24 -0
  7. data/CHANGELOG.md +10 -0
  8. data/Gemfile +8 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +99 -0
  11. data/Rakefile +10 -0
  12. data/bin/console +6 -0
  13. data/bin/setup +6 -0
  14. data/lib/open_api-loader.rb +1 -0
  15. data/lib/open_api/loader.rb +44 -0
  16. data/lib/open_api/loader/collector.rb +61 -0
  17. data/lib/open_api/loader/denormalizer.rb +27 -0
  18. data/lib/open_api/loader/denormalizer/parameters.rb +46 -0
  19. data/lib/open_api/loader/denormalizer/security.rb +35 -0
  20. data/lib/open_api/loader/denormalizer/servers.rb +36 -0
  21. data/lib/open_api/loader/denormalizer/variables.rb +54 -0
  22. data/lib/open_api/loader/reader.rb +47 -0
  23. data/lib/open_api/loader/ref.rb +85 -0
  24. data/lib/open_api/loader/translator.rb +50 -0
  25. data/lib/open_api/loader/translator/clean_definitions.rb +16 -0
  26. data/lib/open_api/loader/translator/convert_bodies.rb +77 -0
  27. data/lib/open_api/loader/translator/convert_forms.rb +71 -0
  28. data/lib/open_api/loader/translator/convert_parameters.rb +135 -0
  29. data/lib/open_api/loader/translator/convert_responses.rb +63 -0
  30. data/lib/open_api/loader/translator/convert_security_schemes.rb +49 -0
  31. data/lib/open_api/loader/translator/convert_servers.rb +46 -0
  32. data/lib/open_api/loader/translator/convert_version.rb +12 -0
  33. data/lib/open_api/loader/translator/denormalize_consumes.rb +44 -0
  34. data/lib/open_api/loader/translator/denormalize_parameters.rb +55 -0
  35. data/lib/open_api/loader/translator/denormalize_produces.rb +53 -0
  36. data/open_api-loader.gemspec +25 -0
  37. data/spec/fixtures/oas2/collected.yaml +1012 -0
  38. data/spec/fixtures/oas2/denormalized.yaml +1462 -0
  39. data/spec/fixtures/oas2/loaded.yaml +564 -0
  40. data/spec/fixtures/oas2/models.yaml +118 -0
  41. data/spec/fixtures/oas2/source.json +1 -0
  42. data/spec/fixtures/oas2/source.yaml +569 -0
  43. data/spec/fixtures/oas2/translated.yaml +1396 -0
  44. data/spec/fixtures/oas3/collected.yaml +233 -0
  45. data/spec/fixtures/oas3/denormalized.yaml +233 -0
  46. data/spec/fixtures/oas3/source.json +1 -0
  47. data/spec/fixtures/oas3/source.yaml +217 -0
  48. data/spec/loader_spec.rb +53 -0
  49. data/spec/spec_helper.rb +17 -0
  50. data/spec/support/fixture_helpers.rb +18 -0
  51. data/spec/support/path_helpers.rb +27 -0
  52. data/spec/unit/collector_spec.rb +31 -0
  53. data/spec/unit/denormalizer_spec.rb +17 -0
  54. data/spec/unit/reader_spec.rb +53 -0
  55. data/spec/unit/ref_spec.rb +107 -0
  56. data/spec/unit/translator_spec.rb +15 -0
  57. metadata +232 -0
@@ -0,0 +1,135 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Adds 'schema', 'style' and 'explode' to values of 'parameters'.
4
+ #
5
+ # @private
6
+ #
7
+ class ConvertParameters < SimpleDelegator
8
+ def call
9
+ parameters.each { |parameter| convert(parameter) }
10
+ end
11
+
12
+ private
13
+
14
+ def parameters
15
+ Enumerator.new do |yielder|
16
+ root_parameters.each { |item| yielder << item }
17
+ path_parameters.each { |item| yielder << item }
18
+ operation_parameters.each { |item| yielder << item }
19
+ headers.each { |item| yielder << item }
20
+ end
21
+ end
22
+
23
+ def convert(item)
24
+ place = item["in"]
25
+ format = item.delete "collectionFormat"
26
+ style = style(place, format)
27
+ explode = explode(format)
28
+ schema = schema(item)
29
+
30
+ item.update("schema" => schema).update(style).update(explode)
31
+ end
32
+
33
+ def params(item)
34
+ Array item.fetch("parameters", [])
35
+ end
36
+
37
+ def root_parameters
38
+ Enumerator.new do |yielder|
39
+ params(self).each { |item| yielder << item if item.is_a? Hash }
40
+ end
41
+ end
42
+
43
+ def path_parameters
44
+ Enumerator.new do |yielder|
45
+ paths.each do |path|
46
+ params(path).each { |item| yielder << item if item.is_a? Hash }
47
+ end
48
+ end
49
+ end
50
+
51
+ def operation_parameters
52
+ Enumerator.new do |yielder|
53
+ operations.each do |operation|
54
+ params(operation).each { |item| yielder << item if item.is_a? Hash }
55
+ end
56
+ end
57
+ end
58
+
59
+ def paths
60
+ Enumerator.new do |yielder|
61
+ fetch("paths", {}).each_value do |path|
62
+ yielder << path if path.is_a? Hash
63
+ end
64
+ end
65
+ end
66
+
67
+ def operations
68
+ Enumerator.new do |yielder|
69
+ paths.each do |path|
70
+ path.each_value { |item| yielder << item if item.is_a? Hash }
71
+ end
72
+ end
73
+ end
74
+
75
+ def responses
76
+ Enumerator.new do |yielder|
77
+ operations.each do |operation|
78
+ operation.fetch("responses", {}).each_value do |item|
79
+ yielder << item if item.is_a? Hash
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def headers
86
+ Enumerator.new do |yielder|
87
+ responses.each do |response|
88
+ response.fetch("headers", {}).each_value do |item|
89
+ yielder << item if item.is_a? Hash
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def style(place, format)
96
+ case format
97
+ when "csv"
98
+ { "style" => (%w[query cookie].include?(place) ? "form" : "simple") }
99
+ when "ssv" then { "style" => "spaceDelimited" }
100
+ when "pipes" then { "style" => "pipeDelimited" }
101
+ when "multi" then { "style" => "form" }
102
+ else {}
103
+ end
104
+ end
105
+
106
+ def explode(format)
107
+ format == "multi" ? { "explode" => true } : {}
108
+ end
109
+
110
+ def schema(item)
111
+ SCHEMA_KEYS.each_with_object({}) do |key, obj|
112
+ obj[key] = item.delete(key) if item.key? key
113
+ end
114
+ end
115
+
116
+ SCHEMA_KEYS = %w[
117
+ default
118
+ enum
119
+ exclusiveMaximum
120
+ exclusiveMinimum
121
+ format
122
+ items
123
+ maxItems
124
+ maxLength
125
+ maximum
126
+ minItems
127
+ minLength
128
+ minimum
129
+ multipleOf
130
+ pattern
131
+ type
132
+ uniqueItems
133
+ ].freeze
134
+ end
135
+ end
@@ -0,0 +1,63 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Builds 'response.content' from 'schema' and 'produces' parameters
4
+ #
5
+ # @private
6
+ #
7
+ class ConvertResponses < SimpleDelegator
8
+ def call
9
+ responses.each { |response| convert(response) }
10
+ end
11
+
12
+ private
13
+
14
+ def paths
15
+ Enumerator.new do |yielder|
16
+ fetch("paths", {}).each_value do |path|
17
+ yielder << path if path.is_a? Hash
18
+ end
19
+ end
20
+ end
21
+
22
+ def operations
23
+ Enumerator.new do |yielder|
24
+ paths.each do |path|
25
+ path.each_value { |item| yielder << item if item.is_a? Hash }
26
+ end
27
+ end
28
+ end
29
+
30
+ def responses
31
+ Enumerator.new do |yielder|
32
+ operations.each do |operation|
33
+ responses = operation["responses"]
34
+ next unless responses.is_a? Hash
35
+ responses.each_value { |item| yielder << item if item.is_a? Hash }
36
+ end
37
+ end
38
+ end
39
+
40
+ def convert(response)
41
+ content_types = Array response.delete("produces")
42
+ schema = Hash response.delete("schema")
43
+ return if content_types.empty?
44
+ response["content"] = {}
45
+ content_types.each do |type|
46
+ response["content"][type] = {
47
+ "schema" => type["/xml"] ? schema : drop_xml(schema)
48
+ }
49
+ end
50
+ end
51
+
52
+ def drop_xml(data)
53
+ case data
54
+ when Hash
55
+ data.each_with_object({}) do |(key, val), obj|
56
+ obj[key] = drop_xml(val) unless key == "xml"
57
+ end
58
+ when Array then data.map { |item| drop_xml(item) }
59
+ else data
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,49 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Wraps the schema and moves its securityDefinitions
4
+ # to components.securitySchemes
5
+ #
6
+ # Translates every securityScheme for oauth2 into new format.
7
+ #
8
+ # @private
9
+ #
10
+ class ConvertSecuritySchemes < SimpleDelegator
11
+ def call
12
+ return unless schemes.is_a? Hash
13
+ convert_oauth_schemes
14
+ self["components"] = { "securitySchemes" => schemes }
15
+ end
16
+
17
+ private
18
+
19
+ def schemes
20
+ @schemes ||= delete("securityDefinitions")
21
+ end
22
+
23
+ def oauth_keys
24
+ @oauth_keys ||= schemes.select do |_, scheme|
25
+ scheme.is_a?(Hash) && scheme["type"] == "oauth2"
26
+ end.keys
27
+ end
28
+
29
+ def convert_oauth_schemes
30
+ oauth_keys.each { |key| convert_scheme(key) }
31
+ end
32
+
33
+ def convert_scheme(key)
34
+ scheme = schemes[key]
35
+ flow = scheme["flow"]
36
+ flow = FLOWS.fetch(flow, flow)
37
+ data = scheme.select { |detail| DETAILS.include? detail }
38
+
39
+ schemes[key] = { "type" => "oauth2", "flows" => { flow => data } }
40
+ end
41
+
42
+ FLOWS = {
43
+ "application" => "clientCredentials",
44
+ "accessCode" => "authorizationCode",
45
+ }.freeze
46
+
47
+ DETAILS = %w[authorizationUrl tokenUrl scopes]
48
+ end
49
+ end
@@ -0,0 +1,46 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Translates OAS2 host, basePath and schemes to OAS3 servers
4
+ # Mutates the source
5
+ #
6
+ # @private
7
+ #
8
+ class ConvertServers < SimpleDelegator
9
+ def call
10
+ convert self
11
+ paths.each { |item| convert item }
12
+ operations.each { |item| convert item }
13
+ end
14
+
15
+ private
16
+
17
+ def paths
18
+ @paths ||= Enumerator.new do |yielder|
19
+ fetch("paths", {}).each_value do |path|
20
+ path.each_value { |item| yielder << item if item.is_a? Hash }
21
+ end
22
+ end
23
+ end
24
+
25
+ def operations
26
+ @operations ||= Enumerator.new do |yielder|
27
+ paths.each do |path|
28
+ path.each_value { |item| yielder << item if item.is_a? Hash }
29
+ end
30
+ end
31
+ end
32
+
33
+ def url
34
+ @url ||= File.join delete("host"), delete("basePath")
35
+ end
36
+
37
+ def convert(item)
38
+ enum = item.delete "schemes"
39
+ return unless enum
40
+ item["servers"] = [{
41
+ "url" => "{scheme}://#{url}",
42
+ "variables" => { "scheme" => { "enum" => Array(enum) } }
43
+ }]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,12 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Wraps the schema and changes version of the OAS
4
+ # @private
5
+ #
6
+ class ConvertVersion < SimpleDelegator
7
+ def call
8
+ delete "swagger"
9
+ self["openapi"] = "3.0.0"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,44 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Denormalizes 'consumes' setting for requestBody
4
+ #
5
+ # @private
6
+ #
7
+ class DenormalizeConsumes < SimpleDelegator
8
+ def call
9
+ paths.each do |path|
10
+ path_consumes = path.delete("consumes") || root_consumes
11
+ operations(path).each do |operation|
12
+ consumes = operation.delete("consumes") || path_consumes
13
+ operation["consumes"] = consumes if consumes?(operation)
14
+ end
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def root_consumes
21
+ @root_consumes ||= delete("consumes")
22
+ end
23
+
24
+ def paths
25
+ Enumerator.new do |yielder|
26
+ fetch("paths", {}).each_value do |item|
27
+ yielder << item if item.is_a? Hash
28
+ end
29
+ end
30
+ end
31
+
32
+ def operations(path)
33
+ Enumerator.new do |yielder|
34
+ path.each_value { |item| yielder << item if item.is_a? Hash }
35
+ end
36
+ end
37
+
38
+ def consumes?(operation)
39
+ Array(operation["parameters"]).any? do |item|
40
+ item.is_a?(Hash) && %w[body formData].include?(item["in"])
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,55 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Denormalizes 'parameters' of 'body' and 'formData' rigth into operations
4
+ # This is needed for building a 'requestBody' part of the schema
5
+ # from 'parameters' and 'consumes'.
6
+ #
7
+ # @private
8
+ #
9
+ class DenormalizeParameters < SimpleDelegator
10
+ def call
11
+ root_params = extract(self)
12
+ paths.each do |path|
13
+ path_params = merge root_params, extract(path)
14
+ operations(path).each do |operation|
15
+ parameters = merge path_params, params(operation)
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 params(data)
38
+ items = data.delete("parameters")
39
+ return [] unless items.is_a? Array
40
+ items.select { |item| item.is_a?(Hash) && item["in"] && item["name"] }
41
+ end
42
+
43
+ def extract(data)
44
+ body, non_body = \
45
+ params(data).partition { |item| %w[body formData].include? item["in"] }
46
+ data["parameters"] = non_body if non_body.any?
47
+ body
48
+ end
49
+
50
+ def merge(left, right)
51
+ names = right.map { |item| item["name"] }
52
+ left.reject { |item| names.include? item["name"] } + right
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,53 @@
1
+ class OpenAPI::Loader::Translator
2
+ #
3
+ # Denormalizes 'consumes' and 'produces' setting
4
+ #
5
+ # @private
6
+ #
7
+ class DenormalizeProduces < SimpleDelegator
8
+ def call
9
+ paths.each do |path|
10
+ path_produces = path.delete("produces") || root_produces
11
+ operations(path).each do |operation|
12
+ produces = operation.delete("produces") || path_produces
13
+ responses(operation).each do |response|
14
+ response["produces"] = produces
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def root_produces
23
+ @root_produces ||= delete("produces")
24
+ end
25
+
26
+ def paths
27
+ Enumerator.new do |yielder|
28
+ fetch("paths", {}).each_value do |item|
29
+ yielder << item if item.is_a? Hash
30
+ end
31
+ end
32
+ end
33
+
34
+ def operations(path)
35
+ Enumerator.new do |yielder|
36
+ path.each_value { |item| yielder << item if item.is_a? Hash }
37
+ end
38
+ end
39
+
40
+ def responses(operation)
41
+ data = operation["responses"]
42
+ if data.is_a? Hash
43
+ Enumerator.new do |yielder|
44
+ data.each_value do |response|
45
+ yielder << response if response.is_a?(Hash) && response["schema"]
46
+ end
47
+ end
48
+ else
49
+ []
50
+ end
51
+ end
52
+ end
53
+ end