openapi3_parser 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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +9 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +4 -0
  7. data/Gemfile +5 -0
  8. data/LICENCE +19 -0
  9. data/README.md +32 -0
  10. data/Rakefile +10 -0
  11. data/TODO.md +30 -0
  12. data/lib/openapi3_parser.rb +39 -0
  13. data/lib/openapi3_parser/context.rb +54 -0
  14. data/lib/openapi3_parser/document.rb +48 -0
  15. data/lib/openapi3_parser/error.rb +5 -0
  16. data/lib/openapi3_parser/fields/map.rb +83 -0
  17. data/lib/openapi3_parser/node.rb +115 -0
  18. data/lib/openapi3_parser/node/map.rb +24 -0
  19. data/lib/openapi3_parser/node/object.rb +28 -0
  20. data/lib/openapi3_parser/node_factories/array.rb +105 -0
  21. data/lib/openapi3_parser/node_factories/callback.rb +26 -0
  22. data/lib/openapi3_parser/node_factories/components.rb +83 -0
  23. data/lib/openapi3_parser/node_factories/contact.rb +24 -0
  24. data/lib/openapi3_parser/node_factories/discriminator.rb +31 -0
  25. data/lib/openapi3_parser/node_factories/encoding.rb +34 -0
  26. data/lib/openapi3_parser/node_factories/example.rb +25 -0
  27. data/lib/openapi3_parser/node_factories/external_documentation.rb +23 -0
  28. data/lib/openapi3_parser/node_factories/header.rb +36 -0
  29. data/lib/openapi3_parser/node_factories/info.rb +28 -0
  30. data/lib/openapi3_parser/node_factories/license.rb +22 -0
  31. data/lib/openapi3_parser/node_factories/link.rb +40 -0
  32. data/lib/openapi3_parser/node_factories/map.rb +110 -0
  33. data/lib/openapi3_parser/node_factories/media_type.rb +44 -0
  34. data/lib/openapi3_parser/node_factories/oauth_flow.rb +24 -0
  35. data/lib/openapi3_parser/node_factories/oauth_flows.rb +31 -0
  36. data/lib/openapi3_parser/node_factories/openapi.rb +50 -0
  37. data/lib/openapi3_parser/node_factories/operation.rb +76 -0
  38. data/lib/openapi3_parser/node_factories/parameter.rb +43 -0
  39. data/lib/openapi3_parser/node_factories/parameter/parameter_like.rb +37 -0
  40. data/lib/openapi3_parser/node_factories/path_item.rb +75 -0
  41. data/lib/openapi3_parser/node_factories/paths.rb +32 -0
  42. data/lib/openapi3_parser/node_factories/reference.rb +33 -0
  43. data/lib/openapi3_parser/node_factories/request_body.rb +31 -0
  44. data/lib/openapi3_parser/node_factories/response.rb +45 -0
  45. data/lib/openapi3_parser/node_factories/responses.rb +32 -0
  46. data/lib/openapi3_parser/node_factories/schema.rb +106 -0
  47. data/lib/openapi3_parser/node_factories/security_requirement.rb +25 -0
  48. data/lib/openapi3_parser/node_factories/security_scheme.rb +35 -0
  49. data/lib/openapi3_parser/node_factories/server.rb +32 -0
  50. data/lib/openapi3_parser/node_factories/server_variable.rb +28 -0
  51. data/lib/openapi3_parser/node_factories/tag.rb +24 -0
  52. data/lib/openapi3_parser/node_factories/xml.rb +25 -0
  53. data/lib/openapi3_parser/node_factory.rb +126 -0
  54. data/lib/openapi3_parser/node_factory/field_config.rb +88 -0
  55. data/lib/openapi3_parser/node_factory/map.rb +39 -0
  56. data/lib/openapi3_parser/node_factory/object.rb +80 -0
  57. data/lib/openapi3_parser/node_factory/object/node_builder.rb +85 -0
  58. data/lib/openapi3_parser/node_factory/object/validator.rb +102 -0
  59. data/lib/openapi3_parser/node_factory/optional_reference.rb +23 -0
  60. data/lib/openapi3_parser/nodes/array.rb +26 -0
  61. data/lib/openapi3_parser/nodes/callback.rb +11 -0
  62. data/lib/openapi3_parser/nodes/components.rb +47 -0
  63. data/lib/openapi3_parser/nodes/contact.rb +23 -0
  64. data/lib/openapi3_parser/nodes/discriminator.rb +19 -0
  65. data/lib/openapi3_parser/nodes/encoding.rb +31 -0
  66. data/lib/openapi3_parser/nodes/example.rb +27 -0
  67. data/lib/openapi3_parser/nodes/external_documentation.rb +19 -0
  68. data/lib/openapi3_parser/nodes/header.rb +13 -0
  69. data/lib/openapi3_parser/nodes/info.rb +35 -0
  70. data/lib/openapi3_parser/nodes/license.rb +19 -0
  71. data/lib/openapi3_parser/nodes/link.rb +35 -0
  72. data/lib/openapi3_parser/nodes/map.rb +11 -0
  73. data/lib/openapi3_parser/nodes/media_type.rb +27 -0
  74. data/lib/openapi3_parser/nodes/oauth_flow.rb +27 -0
  75. data/lib/openapi3_parser/nodes/oauth_flows.rb +27 -0
  76. data/lib/openapi3_parser/nodes/openapi.rb +44 -0
  77. data/lib/openapi3_parser/nodes/operation.rb +59 -0
  78. data/lib/openapi3_parser/nodes/parameter.rb +21 -0
  79. data/lib/openapi3_parser/nodes/parameter/parameter_like.rb +53 -0
  80. data/lib/openapi3_parser/nodes/path_item.rb +59 -0
  81. data/lib/openapi3_parser/nodes/paths.rb +11 -0
  82. data/lib/openapi3_parser/nodes/request_body.rb +23 -0
  83. data/lib/openapi3_parser/nodes/response.rb +27 -0
  84. data/lib/openapi3_parser/nodes/responses.rb +15 -0
  85. data/lib/openapi3_parser/nodes/schema.rb +159 -0
  86. data/lib/openapi3_parser/nodes/security_requirement.rb +11 -0
  87. data/lib/openapi3_parser/nodes/security_scheme.rb +44 -0
  88. data/lib/openapi3_parser/nodes/server.rb +25 -0
  89. data/lib/openapi3_parser/nodes/server_variable.rb +23 -0
  90. data/lib/openapi3_parser/nodes/tag.rb +23 -0
  91. data/lib/openapi3_parser/nodes/xml.rb +31 -0
  92. data/lib/openapi3_parser/validation/error.rb +14 -0
  93. data/lib/openapi3_parser/validation/error_collection.rb +36 -0
  94. data/lib/openapi3_parser/version.rb +5 -0
  95. data/openapi3_parser.gemspec +28 -0
  96. metadata +208 -0
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node_factory/object/validator"
4
+ require "openapi3_parser/error"
5
+
6
+ module Openapi3Parser
7
+ module NodeFactory
8
+ module Object
9
+ class NodeBuilder
10
+ def initialize(input, factory)
11
+ @input = input
12
+ @factory = factory
13
+ end
14
+
15
+ def data
16
+ check_required_fields
17
+ check_unexpected_fields
18
+ check_fields_valid
19
+ data = input.each_with_object({}) do |(key, value), memo|
20
+ memo[key] = value.respond_to?(:node) ? value.node : value
21
+ end
22
+ apply_defaults(data)
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :input, :factory
28
+
29
+ def context
30
+ factory.context
31
+ end
32
+
33
+ def check_required_fields
34
+ fields = Validator.missing_required_fields(input, factory)
35
+ return if fields.empty?
36
+ raise Openapi3Parser::Error,
37
+ "Missing required fields for "\
38
+ "#{context.stringify_namespace}: #{fields.join(', ')}"
39
+ end
40
+
41
+ def check_unexpected_fields
42
+ fields = Validator.unexpected_fields(input, factory)
43
+ return if fields.empty?
44
+ raise Openapi3Parser::Error,
45
+ "Unexpected fields for #{context.stringify_namespace}: "\
46
+ "#{fields.join(', ')}"
47
+ end
48
+
49
+ def check_fields_valid
50
+ factory.field_configs.each do |name, field_config|
51
+ check_type_error(name, field_config)
52
+ check_validation_errors(name, field_config)
53
+ end
54
+ end
55
+
56
+ def check_type_error(name, field_config)
57
+ field_context = context.next_namespace(name)
58
+ error = field_config.input_type_error(input[name], factory)
59
+
60
+ return unless error
61
+ raise Openapi3Parser::Error,
62
+ "Invalid type for "\
63
+ "#{field_context.stringify_namespace}: #{error}"
64
+ end
65
+
66
+ def check_validation_errors(name, field_config)
67
+ field_context = context.next_namespace(name)
68
+ errors = field_config.validation_errors(input[name], factory)
69
+
70
+ return unless errors.any?
71
+ raise Openapi3Parser::Error,
72
+ "Invalid field for #{field_context.stringify_namespace}: "\
73
+ "#{errors.join(', ')}"
74
+ end
75
+
76
+ def apply_defaults(data)
77
+ configs = factory.field_configs
78
+ configs.each_with_object(data) do |(name, field_config), _memo|
79
+ data[name] = field_config.default(factory) if data[name].nil?
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node_factory"
4
+ require "openapi3_parser/validation/error"
5
+
6
+ module Openapi3Parser
7
+ module NodeFactory
8
+ module Object
9
+ class Validator
10
+ def self.missing_required_fields(input, factory)
11
+ configs = factory.field_configs
12
+ configs.each_with_object([]) do |(name, field_config), memo|
13
+ memo << name if field_config.required?(factory) && input[name].nil?
14
+ end
15
+ end
16
+
17
+ def self.unexpected_fields(input, factory)
18
+ unexpected_keys = input.keys - factory.field_configs.keys
19
+ if factory.allowed_extensions?
20
+ unexpected_keys.reject { |k| k =~ NodeFactory::EXTENSION_REGEX }
21
+ else
22
+ unexpected_keys
23
+ end
24
+ end
25
+
26
+ def initialize(input, factory)
27
+ @input = input
28
+ @factory = factory
29
+ end
30
+
31
+ def errors
32
+ [
33
+ missing_required_fields_error,
34
+ unexpected_fields_error,
35
+ invalid_field_errors
36
+ ].flatten.compact
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :input, :factory
42
+
43
+ def context
44
+ factory.context
45
+ end
46
+
47
+ def missing_required_fields_error
48
+ fields = self.class.missing_required_fields(input, factory)
49
+ return unless fields.any?
50
+ Validation::Error.new(
51
+ context.namespace,
52
+ "Missing required fields: #{fields.join(', ')}"
53
+ )
54
+ end
55
+
56
+ def unexpected_fields_error
57
+ fields = self.class.unexpected_fields(input, factory)
58
+ return unless fields.any?
59
+ Validation::Error.new(
60
+ context.namespace,
61
+ "Unexpected fields: #{fields.join(', ')}"
62
+ )
63
+ end
64
+
65
+ def invalid_field_errors
66
+ factory.field_configs.inject([]) do |memo, (name, field_config)|
67
+ memo + field_errors(name, field_config)
68
+ end
69
+ end
70
+
71
+ def field_errors(name, field_config)
72
+ field = input[name]
73
+ return [] if field.nil?
74
+ return field.errors.to_a if field.respond_to?(:errors)
75
+ type_error = build_type_error(name, field_config)
76
+ return [type_error] if type_error
77
+ build_validation_errors(name, field_config)
78
+ end
79
+
80
+ def build_type_error(name, field_config)
81
+ type_error = field_config.input_type_error(input[name], factory)
82
+ return unless type_error
83
+ Validation::Error.new(
84
+ context.next_namespace(name),
85
+ "Invalid type: #{type_error}"
86
+ )
87
+ end
88
+
89
+ def build_validation_errors(name, field_config)
90
+ field_config.validation_errors(
91
+ input[name], factory
92
+ ).map do |error|
93
+ Validation::Error.new(
94
+ context.next_namespace(name),
95
+ "Invalid field: #{error}"
96
+ )
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node_factories/reference"
4
+
5
+ module Openapi3Parser
6
+ module NodeFactory
7
+ class OptionalReference
8
+ def initialize(factory)
9
+ @factory = factory
10
+ end
11
+
12
+ def call(context)
13
+ reference = context.input.is_a?(Hash) && context.input["$ref"]
14
+ return NodeFactories::Reference.new(context, self) if reference
15
+ factory.new(context)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :factory
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/map"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Array
8
+ include Enumerable
9
+
10
+ attr_reader :node_data, :node_context
11
+
12
+ def initialize(data, context)
13
+ @node_data = data
14
+ @node_context = context
15
+ end
16
+
17
+ def [](value)
18
+ node_data[value]
19
+ end
20
+
21
+ def each(&block)
22
+ node_data.each(&block)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/map"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Callback
8
+ include Node::Map
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Components
8
+ include Node::Object
9
+
10
+ def schemas
11
+ node_data["schemas"]
12
+ end
13
+
14
+ def responses
15
+ node_data["responses"]
16
+ end
17
+
18
+ def parameters
19
+ node_data["parameters"]
20
+ end
21
+
22
+ def examples
23
+ node_data["examples"]
24
+ end
25
+
26
+ def request_bodies
27
+ node_data["requestBodies"]
28
+ end
29
+
30
+ def headers
31
+ node_data["headers"]
32
+ end
33
+
34
+ def security_schemes
35
+ node_data["securitySchemes"]
36
+ end
37
+
38
+ def links
39
+ node_data["links"]
40
+ end
41
+
42
+ def callbacks
43
+ node_data["callbacks"]
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Contact
8
+ include Node::Object
9
+
10
+ def name
11
+ data["name"]
12
+ end
13
+
14
+ def url
15
+ data["url"]
16
+ end
17
+
18
+ def email
19
+ data["email"]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Discriminator
8
+ include Node::Object
9
+
10
+ def property_name
11
+ node_data["propertyName"]
12
+ end
13
+
14
+ def mapping
15
+ node_data["mapping"]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Encoding
8
+ include Node::Object
9
+
10
+ def content_type
11
+ node_data["contentType"]
12
+ end
13
+
14
+ def headers
15
+ node_data["headers"]
16
+ end
17
+
18
+ def style
19
+ node_data["style"]
20
+ end
21
+
22
+ def explode?
23
+ node_data["explode"]
24
+ end
25
+
26
+ def allow_reserved?
27
+ node_data["allowReserved"]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Example
8
+ include Node::Object
9
+
10
+ def summary
11
+ node_data["summary"]
12
+ end
13
+
14
+ def description
15
+ node_data["description"]
16
+ end
17
+
18
+ def value
19
+ node_data["value"]
20
+ end
21
+
22
+ def external_value
23
+ node_data["externalValue"]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class ExternalDocumentation
8
+ include Node::Object
9
+
10
+ def description
11
+ node_data["description"]
12
+ end
13
+
14
+ def url
15
+ node_data["url"]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+ require "openapi3_parser/nodes/parameter/parameter_like"
5
+
6
+ module Openapi3Parser
7
+ module Nodes
8
+ class Header
9
+ include Node::Object
10
+ include Parameter::ParameterLike
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/object"
4
+
5
+ module Openapi3Parser
6
+ module Nodes
7
+ class Info
8
+ include Node::Object
9
+
10
+ def title
11
+ node_data["title"]
12
+ end
13
+
14
+ def description
15
+ node_data["description"]
16
+ end
17
+
18
+ def terms_of_service
19
+ node_data["termsOfService"]
20
+ end
21
+
22
+ def contact
23
+ node_data["contact"]
24
+ end
25
+
26
+ def license
27
+ node_data["license"]
28
+ end
29
+
30
+ def version
31
+ node_data["version"]
32
+ end
33
+ end
34
+ end
35
+ end