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,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/nodes/server"
4
+ require "openapi3_parser/node_factory/object"
5
+ require "openapi3_parser/node_factories/server_variable"
6
+ require "openapi3_parser/node_factories/map"
7
+
8
+ module Openapi3Parser
9
+ module NodeFactories
10
+ class Server
11
+ include NodeFactory::Object
12
+
13
+ allow_extensions
14
+ field "url", input_type: String, required: true
15
+ field "description", input_type: String
16
+ field "variables", factory: :variables_factory
17
+
18
+ private
19
+
20
+ def build_object(data, context)
21
+ Nodes::Server.new(data, context)
22
+ end
23
+
24
+ def variables_factory(context)
25
+ NodeFactories::Map.new(
26
+ context,
27
+ value_factory: NodeFactories::ServerVariable
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/nodes/server_variable"
4
+ require "openapi3_parser/node_factory/object"
5
+
6
+ module Openapi3Parser
7
+ module NodeFactories
8
+ class ServerVariable
9
+ include NodeFactory::Object
10
+
11
+ allow_extensions
12
+ field "enum", input_type: ::Array, validate: :validate_enum
13
+ field "default", input_type: String, required: true
14
+ field "description", input_type: String
15
+
16
+ private
17
+
18
+ def validate_enum(input)
19
+ return "Expected atleast one value" if input.empty?
20
+ "Expected String values" unless input.map(&:class).uniq == [String]
21
+ end
22
+
23
+ def build_object(data, context)
24
+ Nodes::ServerVariable.new(data, context)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/nodes/tag"
4
+ require "openapi3_parser/node_factory/object"
5
+ require "openapi3_parser/node_factories/external_documentation"
6
+
7
+ module Openapi3Parser
8
+ module NodeFactories
9
+ class Tag
10
+ include NodeFactory::Object
11
+
12
+ allow_extensions
13
+ field "name", input_type: String, required: true
14
+ field "description", input_type: String
15
+ field "externalDocs", factory: NodeFactories::ExternalDocumentation
16
+
17
+ private
18
+
19
+ def build_object(data, context)
20
+ Nodes::Tag.new(data, context)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/nodes/xml"
4
+ require "openapi3_parser/node_factory/object"
5
+
6
+ module Openapi3Parser
7
+ module NodeFactories
8
+ class Xml
9
+ include NodeFactory::Object
10
+
11
+ allow_extensions
12
+ field "name", input_type: String
13
+ field "namespace", input_type: String
14
+ field "prefix", input_type: String
15
+ field "attribute", input_type: :boolean, default: false
16
+ field "wrapped", input_type: :boolean, default: false
17
+
18
+ private
19
+
20
+ def build_object(data, context)
21
+ Nodes::Xml.new(data, context)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/error"
4
+ require "openapi3_parser/validation/error"
5
+ require "openapi3_parser/validation/error_collection"
6
+
7
+ module Openapi3Parser
8
+ module NodeFactory
9
+ module ClassMethods
10
+ def input_type(type)
11
+ @input_type = type
12
+ end
13
+
14
+ def valid_input_type?(type)
15
+ return true unless @input_type
16
+ type.is_a?(@input_type)
17
+ end
18
+
19
+ def expected_input_type
20
+ @input_type
21
+ end
22
+ end
23
+
24
+ def self.included(base)
25
+ base.extend(ClassMethods)
26
+ end
27
+
28
+ EXTENSION_REGEX = /^x-(.*)/
29
+
30
+ attr_reader :context
31
+
32
+ def initialize(context)
33
+ @context = context
34
+ end
35
+
36
+ def valid?
37
+ errors.empty?
38
+ end
39
+
40
+ def errors
41
+ @errors ||= build_errors
42
+ end
43
+
44
+ def node
45
+ @node ||= build_valid_node
46
+ end
47
+
48
+ private
49
+
50
+ def validate(_input, _context); end
51
+
52
+ def validate_input(error_collection)
53
+ add_validation_errors(
54
+ validate(context.input, context),
55
+ error_collection
56
+ )
57
+ end
58
+
59
+ def add_validation_errors(errors, error_collection)
60
+ errors = Array(errors)
61
+ errors.each do |error|
62
+ unless error.is_a?(Validation::Error)
63
+ error = Validation::Error.new(context.namespace, error)
64
+ end
65
+ error_collection.append(error)
66
+ end
67
+ error_collection
68
+ end
69
+
70
+ def build_errors
71
+ error_collection = Validation::ErrorCollection.new
72
+ unless valid_type?
73
+ error = Validation::Error.new(
74
+ context.namespace, "Invalid type. #{validate_type}"
75
+ )
76
+ return error_collection.tap { |ec| ec.append(error) }
77
+ end
78
+ validate_input(error_collection)
79
+ error_collection
80
+ end
81
+
82
+ def build_valid_node
83
+ unless valid_type?
84
+ raise Openapi3Parser::Error,
85
+ "Invalid type for #{context.stringify_namespace}. "\
86
+ "#{validate_type}"
87
+ end
88
+
89
+ validate_before_build
90
+ build_node(processed_input)
91
+ end
92
+
93
+ def validate_before_build
94
+ errors = Array(validate(context.input, context))
95
+ return unless errors.any?
96
+ raise Openapi3Parser::Error,
97
+ "Invalid data for #{context.stringify_namespace}. "\
98
+ "#{errors.join(', ')}"
99
+ end
100
+
101
+ def valid_type?
102
+ validate_type.nil?
103
+ end
104
+
105
+ def validate_type
106
+ valid_type = self.class.valid_input_type?(context.input)
107
+ return "Expected #{self.class.expected_input_type}" unless valid_type
108
+ end
109
+
110
+ def processed_input
111
+ @processed_input ||= process_input(context.input)
112
+ end
113
+
114
+ def process_input(input)
115
+ input
116
+ end
117
+
118
+ def build_node(input)
119
+ input
120
+ end
121
+
122
+ def extension?(key)
123
+ key.match(EXTENSION_REGEX)
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/error"
4
+
5
+ module Openapi3Parser
6
+ module NodeFactory
7
+ class FieldConfig
8
+ attr_reader :given_input_type, :given_factory, :given_required,
9
+ :given_default, :given_validate
10
+
11
+ def initialize(
12
+ input_type: nil,
13
+ factory: nil,
14
+ required: false,
15
+ default: nil,
16
+ validate: nil
17
+ )
18
+ @given_input_type = input_type
19
+ @given_factory = factory
20
+ @given_required = required
21
+ @given_default = default
22
+ @given_validate = validate
23
+ end
24
+
25
+ def factory?
26
+ !given_factory.nil?
27
+ end
28
+
29
+ def initialize_factory(context, factory)
30
+ if given_factory.is_a?(Class)
31
+ given_factory.new(context)
32
+ elsif given_factory.is_a?(Symbol)
33
+ factory.send(given_factory, context)
34
+ else
35
+ given_factory.call(context)
36
+ end
37
+ end
38
+
39
+ def required?(_factory)
40
+ given_required
41
+ end
42
+
43
+ def input_type_error(input, factory)
44
+ return if !given_input_type || input.nil?
45
+ return boolean_error(input) if given_input_type == :boolean
46
+ resolve_type_error(input, factory)
47
+ end
48
+
49
+ def validation_errors(input, factory)
50
+ return [] if !given_validate || input.nil?
51
+ errors = resolve_validation_errors(input, factory)
52
+ Array(errors)
53
+ end
54
+
55
+ def default(factory)
56
+ return given_default.call if given_default.is_a?(Proc)
57
+ return factory.send(given_default) if given_default.is_a?(Symbol)
58
+ given_default
59
+ end
60
+
61
+ private
62
+
63
+ def boolean_error(input)
64
+ "Expected a boolean" unless [true, false].include?(input)
65
+ end
66
+
67
+ def resolve_type_error(input, factory)
68
+ if given_input_type.is_a?(Proc)
69
+ given_input_type.call(input)
70
+ elsif given_input_type.is_a?(Symbol)
71
+ factory.send(given_input_type, input)
72
+ elsif !input.is_a?(given_input_type)
73
+ "Expected a #{given_input_type}"
74
+ end
75
+ end
76
+
77
+ def resolve_validation_errors(input, factory)
78
+ if given_validate.is_a?(Proc)
79
+ given_validate.call(input)
80
+ elsif given_validate.is_a?(Symbol)
81
+ factory.send(given_validate, input)
82
+ else
83
+ raise Error, "Expected a Proc or Symbol for validate"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node_factory"
4
+
5
+ module Openapi3Parser
6
+ module NodeFactory
7
+ module Map
8
+ include NodeFactory
9
+
10
+ def self.included(base)
11
+ base.extend(NodeFactory::ClassMethods)
12
+ base.class_eval do
13
+ input_type Hash
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def build_node(input)
20
+ data = input.each_with_object({}) do |(key, value), memo|
21
+ memo[key] = value.respond_to?(:node) ? value.node : value
22
+ end
23
+ build_map(data, context)
24
+ end
25
+
26
+ def validate_input(error_collection)
27
+ super(error_collection)
28
+ processed_input.each_value do |value|
29
+ next unless value.respond_to?(:errors)
30
+ error_collection.merge(value.errors)
31
+ end
32
+ end
33
+
34
+ def build_map(data, _)
35
+ data
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node_factory"
4
+ require "openapi3_parser/node_factory/field_config"
5
+ require "openapi3_parser/node_factory/object/node_builder"
6
+ require "openapi3_parser/node_factory/object/validator"
7
+
8
+ module Openapi3Parser
9
+ module NodeFactory
10
+ module Object
11
+ include NodeFactory
12
+
13
+ module ClassMethods
14
+ def field(name, **options)
15
+ @field_configs ||= {}
16
+ @field_configs[name] = FieldConfig.new(options)
17
+ end
18
+
19
+ def field_configs
20
+ @field_configs || {}
21
+ end
22
+
23
+ def allow_extensions
24
+ @allow_extensions = true
25
+ end
26
+
27
+ def disallow_extensions
28
+ @allow_extensions = false
29
+ end
30
+
31
+ def allowed_extensions?
32
+ @allow_extensions == true
33
+ end
34
+ end
35
+
36
+ def self.included(base)
37
+ base.extend(NodeFactory::ClassMethods)
38
+ base.extend(ClassMethods)
39
+ base.class_eval do
40
+ input_type Hash
41
+ end
42
+ end
43
+
44
+ def allowed_extensions?
45
+ self.class.allowed_extensions?
46
+ end
47
+
48
+ def field_configs
49
+ self.class.field_configs || {}
50
+ end
51
+
52
+ private
53
+
54
+ def process_input(input)
55
+ field_configs.each_with_object(input.dup) do |(field, config), memo|
56
+ next if !config.factory? || !memo[field]
57
+ next_context = context.next_namespace(field)
58
+ memo[field] = config.initialize_factory(
59
+ next_context, self
60
+ )
61
+ end
62
+ end
63
+
64
+ def validate_input(error_collection)
65
+ super(error_collection)
66
+ validator = Validator.new(processed_input, self)
67
+ error_collection.tap { |ec| ec.append(*validator.errors) }
68
+ end
69
+
70
+ def build_node(input)
71
+ data = NodeBuilder.new(input, self).data
72
+ build_object(data, context)
73
+ end
74
+
75
+ def build_object(data, _context)
76
+ data
77
+ end
78
+ end
79
+ end
80
+ end