dry-swagger 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ea8396c1d7976844254a7e7564a74a0cdc0391c3bf42a4aa49fc479fd99e11e
4
- data.tar.gz: 611a49427c0c0ece4bd4ec1e134afd8bcebc01f43b0803eecabdd9cf16df4d14
3
+ metadata.gz: c52c6906b862ea3104ef5d841db24a69f38bbaa5cbda13ef553305453e1665cc
4
+ data.tar.gz: 5887800bdbcccd0603aad3cecba5c593391be2644456cbf4437557aa039ca923
5
5
  SHA512:
6
- metadata.gz: 7d1e8404d230897c6f4e88fffbd1870ee1fbf305b725d8d9067f5529c3f2bcdda71990b364372478d3ff9f780e6a997144c557f57f574768d705caea10255923
7
- data.tar.gz: 0d7b5f769a536d9fd18b594cf097c5c9922b92f74bdf009ff2dd90ea099e90c5bc9d36aa684efcdd49a1176f299d6db2574a786a8a98213a5301a15bfdb223bf
6
+ metadata.gz: a1c2b6b524bf1a06641b7769c90c40246ef27b970177ddcd81d355c7da38435ea32329eb692107dba85ba00d757e234271a5d72bed167dcc2903e1a0c0312f3e
7
+ data.tar.gz: cbad39dd938d175ca9713d3a4cb834943d83ceb8efe5507e3c93e9cfb231a70251695fd40bb8eeee3a02278472fcfee603826ef04f3a627c915290f85057635d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dry-swagger (0.3.0)
4
+ dry-swagger (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -261,18 +261,22 @@ Or install it yourself as:
261
261
  ## Custom Configuration For Your Project
262
262
  You can override default configurations by creating a file in config/initializers/dry-swagger.rb and changing the following values.
263
263
 
264
- Dry::Swagger.configuration do |config|
265
- config.struct_enable_required_validation = true / false
266
- config.struct_enable_nullable_validation = true / false
267
- config.struct_enable_enums = true / false
268
- config.struct_enable_descriptions = true / false
269
-
270
- config.contract_enable_required_validation = true / false
271
- config.contract_enable_nullable_validation = true / false
272
- config.contract_enable_enums = true / false
273
- config.contract_enable_descriptions = true / false
264
+ Dry::Swagger::Config::StructConfiguration.configuration do |config|
265
+ config.enable_required_validation = true / false
266
+ config.enable_nullable_validation = true / false
267
+ config.enable_enums = true / false
268
+ config.enable_descriptions = true / false
274
269
  config.nullable_type = :"x-nullable" / :nullable
275
270
  end
271
+
272
+ Dry::Swagger::Config::ContractConfiguration.configuration do |config|
273
+ config.enable_required_validation = true / false
274
+ config.enable_nullable_validation = true / false
275
+ config.enable_enums = true / false
276
+ config.enable_descriptions = true / false
277
+ config.nullable_type = :"x-nullable" / :nullable
278
+ end
279
+
276
280
  By default, all these settings are true, and nullable_type is :"x-nullable".
277
281
  ## Development
278
282
 
data/lib/dry/swagger.rb CHANGED
@@ -1,24 +1,14 @@
1
1
  require "dry/swagger/version"
2
2
  require "dry/swagger/contract_parser"
3
3
  require "dry/swagger/struct_parser"
4
- require 'helpers/configuration'
4
+ require 'dry/swagger/documentation_generator'
5
+ require 'dry/swagger/errors/missing_hash_schema_error'
6
+ require 'dry/swagger/errors/missing_type_error'
7
+ require 'dry/swagger/config/configuration'
8
+ require 'dry/swagger/config/contract_configuration'
9
+ require 'dry/swagger/config/struct_configuration'
5
10
 
6
11
  module Dry
7
12
  module Swagger
8
- class Error < StandardError; end
9
-
10
- extend Configuration
11
-
12
- define_setting :struct_enable_required_validation, true
13
- define_setting :struct_enable_nullable_validation, true
14
- define_setting :struct_enable_enums, true
15
- define_setting :struct_enable_descriptions, true
16
-
17
- define_setting :contract_enable_required_validation, true
18
- define_setting :contract_enable_nullable_validation, true
19
- define_setting :contract_enable_enums, true
20
- define_setting :contract_enable_descriptions, true
21
-
22
- define_setting :nullable_type, :"x-nullable"
23
13
  end
24
14
  end
@@ -0,0 +1,32 @@
1
+ module Dry
2
+ module Swagger
3
+ module Config
4
+ module Configuration
5
+ def configuration
6
+ yield self
7
+ end
8
+
9
+ def define_setting(name, default = nil)
10
+ class_variable_set("@@#{name}", default)
11
+
12
+ define_class_method "#{name}=" do |value|
13
+ class_variable_set("@@#{name}", value)
14
+ end
15
+
16
+ define_class_method name do
17
+ class_variable_get("@@#{name}")
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def define_class_method(name, &block)
24
+ (class << self; self; end).instance_eval do
25
+ define_method name, &block
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ module Dry
2
+ module Swagger
3
+ module Config
4
+ module ContractConfiguration
5
+ extend Configuration
6
+
7
+ define_setting :enable_required_validation, true
8
+ define_setting :enable_nullable_validation, true
9
+ define_setting :enable_enums, true
10
+ define_setting :enable_descriptions, true
11
+ define_setting :nullable_type, :"x-nullable"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Dry
2
+ module Swagger
3
+ module Config
4
+ module StructConfiguration
5
+ extend Configuration
6
+
7
+ define_setting :enable_required_validation, true
8
+ define_setting :enable_nullable_validation, true
9
+ define_setting :enable_enums, true
10
+ define_setting :enable_descriptions, true
11
+ define_setting :nullable_type, :"x-nullable"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -15,22 +15,13 @@ module Dry
15
15
  time?: 'time'
16
16
  }.freeze
17
17
 
18
- SWAGGER_FIELD_TYPE_DEFINITIONS = {
19
- "string" => { type: :string },
20
- "integer" => { type: :integer },
21
- "boolean" => { type: :boolean },
22
- "float" => { type: :float },
23
- "datetime" => { type: :string, format: :datetime },
24
- "date" => { type: :string, format: :date },
25
- "time" => { type: :string, format: :time },
26
- }.freeze
27
-
28
18
  # @api private
29
19
  attr_reader :keys
30
20
 
31
21
  # @api private
32
22
  def initialize
33
23
  @keys = {}
24
+ @config = Config::ContractConfiguration
34
25
  end
35
26
 
36
27
  # @api private
@@ -43,7 +34,7 @@ module Dry
43
34
  @keys = {}
44
35
  visit(contract.schema.to_ast)
45
36
  instance_eval(&block) if block_given?
46
- to_swagger
37
+ self
47
38
  end
48
39
 
49
40
  # @api private
@@ -75,14 +66,13 @@ module Dry
75
66
  end
76
67
 
77
68
  def visit_not(_node, opts = {})
78
- key = opts[:key]
79
- keys[key][::Dry::Swagger.nullable_type] = true if ::Dry::Swagger.contract_enable_nullable_validation
69
+ keys[opts[:key]][@config.nullable_type] = true
80
70
  end
81
71
 
82
72
  # @api private
83
73
  def visit_implication(node, opts = {})
84
74
  node.each do |el|
85
- opts = opts.merge(required: false) if ::Dry::Swagger.contract_enable_required_validation
75
+ opts = opts.merge(required: false)
86
76
  visit(el, opts)
87
77
  end
88
78
  end
@@ -96,7 +86,7 @@ module Dry
96
86
  def visit_key(node, opts = {})
97
87
  name, rest = node
98
88
  opts = opts.merge(key: name)
99
- opts = opts.merge(required: true) if ::Dry::Swagger.contract_enable_required_validation
89
+ opts = opts.merge(required: true)
100
90
  visit(rest, opts)
101
91
  end
102
92
 
@@ -107,11 +97,13 @@ module Dry
107
97
  key = opts[:key]
108
98
 
109
99
  if name.equal?(:key?)
110
- keys[rest[0][1]] = { required: opts.fetch(:required, true) } if ::Dry::Swagger.contract_enable_required_validation
100
+ keys[rest[0][1]] = { required: opts.fetch(:required, true) }
111
101
  elsif name.equal?(:array?)
112
102
  keys[key][:array] = true
113
103
  elsif name.equal?(:included_in?)
114
- keys[key][:enum] = rest[0][1]
104
+ enums = rest[0][1]
105
+ enums += [nil] if opts.fetch(@config.nullable_type, false)
106
+ keys[key][:enum] = enums
115
107
  elsif PREDICATE_TO_TYPE[name]
116
108
  keys[key][:type] = PREDICATE_TO_TYPE[name]
117
109
  else
@@ -139,34 +131,7 @@ module Dry
139
131
  end
140
132
 
141
133
  def to_swagger
142
- generate_documentation(keys)
143
- end
144
-
145
- private
146
-
147
- def generate_documentation(fields)
148
- documentation = { properties: {}, required: [] }
149
- fields.each do |field_name, attributes_hash|
150
- documentation[:properties][field_name] = generate_field_properties(attributes_hash)
151
- documentation[:required] << field_name if ::Dry::Swagger.contract_enable_required_validation && attributes_hash[:required]
152
- end
153
- { :type => :object, :properties => documentation[:properties], :required => documentation[:required] }
154
- end
155
-
156
- def generate_field_properties(attributes_hash)
157
- if attributes_hash[:type] == 'array'
158
- { type: :array, items: generate_documentation(attributes_hash[:keys]) }
159
- elsif attributes_hash[:array] && attributes_hash[:type] != 'array'
160
- { type: :array, items: SWAGGER_FIELD_TYPE_DEFINITIONS.fetch(attributes_hash[:type]) }
161
- elsif attributes_hash[:type] == 'hash'
162
- generate_documentation(attributes_hash[:keys])
163
- else
164
- field = SWAGGER_FIELD_TYPE_DEFINITIONS.fetch(attributes_hash[:type])
165
- field = field.merge(::Dry::Swagger.nullable_type => attributes_hash[::Dry::Swagger.nullable_type] | false) if ::Dry::Swagger.contract_enable_nullable_validation
166
- field = field.merge(enum: attributes_hash[:enum]) if attributes_hash[:enum] if ::Dry::Swagger.contract_enable_enums
167
- field = field.merge(description: attributes_hash[:description]) if attributes_hash[:description] if ::Dry::Swagger.contract_enable_descriptions
168
- field
169
- end
134
+ DocumentationGenerator.new(@config).generate_documentation(keys)
170
135
  end
171
136
  end
172
137
  end
@@ -0,0 +1,58 @@
1
+ module Dry
2
+ module Swagger
3
+ class DocumentationGenerator
4
+ SWAGGER_FIELD_TYPE_DEFINITIONS = {
5
+ "string" => { type: :string },
6
+ "integer" => { type: :integer },
7
+ "boolean" => { type: :boolean },
8
+ "float" => { type: :float },
9
+ "datetime" => { type: :string, format: :datetime },
10
+ "date" => { type: :string, format: :date },
11
+ "time" => { type: :string, format: :time },
12
+ }.freeze
13
+
14
+ def initialize(config)
15
+ @config = config
16
+ end
17
+
18
+ def generate_documentation(fields)
19
+ documentation = { properties: {}, required: [] }
20
+ fields.each do |field_name, attributes_hash|
21
+ documentation[:properties][field_name] = generate_field_properties(attributes_hash)
22
+ documentation[:required] << field_name if attributes_hash[:required] && @config.enable_required_validation
23
+
24
+ rescue Errors::MissingTypeError => e
25
+ raise StandardError.new e.message % { field_name: field_name, valid_types: SWAGGER_FIELD_TYPE_DEFINITIONS.keys, attributes_hash: attributes_hash }
26
+ rescue Errors::MissingHashSchemaError => e
27
+ raise StandardError.new e.message % { field_name: field_name, valid_types: SWAGGER_FIELD_TYPE_DEFINITIONS.keys, attributes_hash: attributes_hash }
28
+ end
29
+
30
+ { :type => :object, :properties => documentation[:properties], :required => documentation[:required] }
31
+ end
32
+
33
+ def generate_field_properties(attributes_hash)
34
+ if attributes_hash[:type] == 'array'
35
+ items = generate_documentation(attributes_hash[:keys])
36
+ items.merge(@config.nullable_type => attributes_hash[@config.nullable_type] | false) if @config.enable_nullable_validation
37
+ { type: :array, items: items }
38
+ elsif attributes_hash[:array] && attributes_hash[:type] != 'array'
39
+ items = SWAGGER_FIELD_TYPE_DEFINITIONS.fetch(attributes_hash[:type])
40
+ items = items.merge(@config.nullable_type => attributes_hash[@config.nullable_type] | false) if @config.enable_nullable_validation
41
+ { type: :array, items: items }
42
+ elsif attributes_hash[:type] == 'hash'
43
+ raise Errors::MissingHashSchemaError.new unless attributes_hash[:keys]
44
+ generate_documentation(attributes_hash[:keys])
45
+ else
46
+ field = SWAGGER_FIELD_TYPE_DEFINITIONS.fetch(attributes_hash[:type])
47
+ field = field.merge(@config.nullable_type => attributes_hash[@config.nullable_type] | false) if @config.enable_nullable_validation
48
+ field = field.merge(enum: attributes_hash[:enum]) if attributes_hash[:enum] if @config.enable_enums
49
+ field = field.merge(description: attributes_hash[:description]) if attributes_hash[:description] if @config.enable_descriptions
50
+ field
51
+ end
52
+
53
+ rescue KeyError
54
+ raise Errors::MissingTypeError.new
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,15 @@
1
+ module Dry
2
+ module Swagger
3
+ module Errors
4
+ class MissingHashSchemaError < StandardError
5
+ def message
6
+ "Could not generate documentation for field %{field_name}. The field is defined as hash,
7
+ but the schema is not defined.
8
+ Valid types are: %{valid_types}.
9
+ The parser has generated the following definition for the field: %{field_name}: %{attributes_hash}
10
+ "
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Dry
2
+ module Swagger
3
+ module Errors
4
+ class MissingTypeError < StandardError
5
+ def message
6
+ "Could not generate documentation for field %{field_name}. The field is missing a type.
7
+ If the field you have defined is an array, you must specify the type of the elements in that array.
8
+ Valid types are: %{valid_types}.
9
+ The parser has generated the following definition for the field: %{field_name}: %{attributes_hash}.
10
+ "
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,81 +1,132 @@
1
1
  module Dry
2
2
  module Swagger
3
3
  class StructParser
4
- SWAGGER_FIELD_TYPE_DEFINITIONS = {
5
- "String" => { type: :string },
6
- "Integer" => { type: :integer },
7
- "TrueClass | FalseClass" => { type: :boolean },
8
- "BigDecimal" => { type: :decimal },
9
- "Float" => { type: :float },
10
- "DateTime" => { type: :string, format: :datetime },
11
- "Date" => { type: :string, format: :date },
12
- "Time" => { type: :string, format: :time }
13
- }
14
-
15
- def call(dto)
16
- documentation = generate_fields_documentation(dto.schema)
17
- { :type => :object, :properties => documentation[:properties], :required => documentation[:required]}
4
+ PREDICATE_TYPES = {
5
+ String: 'string',
6
+ Integer: 'integer',
7
+ Bool: 'boolean',
8
+ Float: 'float',
9
+ Date: 'date',
10
+ DateTime: 'datetime',
11
+ Time: 'time'
12
+ }.freeze
13
+
14
+ attr_reader :keys
15
+
16
+ def initialize
17
+ @keys = {}
18
+ @config = Config::StructConfiguration
18
19
  end
19
20
 
20
- def generate_fields_documentation(dto_schema)
21
- documentation = { properties: {}, required: [] }
22
- dto_schema.name_key_map.each do |name, schema_key_object|
23
- documentation[:properties][name] = schema_key_object.type.optional? ?
24
- generate_field_properties(schema_key_object.type.right, true) :
25
- generate_field_properties(schema_key_object.type, false)
21
+ def to_h
22
+ { keys: keys }
23
+ end
26
24
 
27
- documentation[:required] << name if ::Dry::Swagger.struct_enable_required_validation && schema_key_object.required?
28
- end
29
- documentation
25
+ def call(dto, &block)
26
+ @keys = {}
27
+ visit(dto.schema.to_ast)
28
+ instance_eval(&block) if block_given?
29
+ self
30
+ end
31
+
32
+ def visit(node, opts = {})
33
+ meth, rest = node
34
+ public_send(:"visit_#{meth}", rest, opts)
35
+ end
36
+
37
+ def visit_constructor(node, opts = {})
38
+ visit(node[0], opts)
30
39
  end
31
40
 
32
- def generate_field_properties(type, nullable)
33
- field_type = type.name
34
- if SWAGGER_FIELD_TYPE_DEFINITIONS[field_type] # IS PRIMITIVE FIELD?
35
- definition = SWAGGER_FIELD_TYPE_DEFINITIONS[field_type]
36
- definition = definition
37
- if is_enum?(type.class.name) && ::Dry::Swagger.struct_enable_enums
38
- enums = type.values
39
- enums += [nil] if nullable
40
- definition = definition.merge(enum: enums)
41
- end
42
- elsif is_array?(field_type)
43
- definition = { type: :array }
44
- if is_primitive?(type.type.member.name)
45
- definition = definition
46
- definition = definition.merge(items: SWAGGER_FIELD_TYPE_DEFINITIONS[type.type.member.name])
47
- else
48
- schema = is_array_with_dynamic_schema?(type.type.member) ? type.type.member.left : type.type.member
49
- definition = definition.merge(items: call(schema))
50
- end
51
- else
52
- schema = is_dynamic_schema?(type) ? type.left : type
53
- definition = call(schema)
41
+ def visit_schema(node, opts = {})
42
+ target = (key = opts[:key]) ? self.class.new : self
43
+
44
+ required = opts.fetch(:required, true)
45
+ nullable = opts.fetch(:nullable, false)
46
+
47
+ node[0].each do |child|
48
+ target.visit(child)
54
49
  end
55
50
 
56
- ::Dry::Swagger.struct_enable_nullable_validation ? definition.merge(::Dry::Swagger.nullable_type => nullable) : definition
51
+ return unless key
52
+
53
+ target_info = target.to_h if opts[:member]
54
+
55
+ type = opts[:array]? 'array' : 'hash'
56
+
57
+ keys[key] = {
58
+ type: type,
59
+ required: required,
60
+ @config.nullable_type => nullable,
61
+ **target_info
62
+ }
63
+ end
64
+
65
+ def visit_key(node, opts = {})
66
+ name, required, rest = node
67
+ opts[:key] = name
68
+ opts[:required] = required
69
+ visit(rest, opts)
70
+ end
71
+
72
+ def visit_constrained(node, opts = {})
73
+ node.each {|it| visit(it, opts) }
74
+ end
75
+
76
+ def visit_nominal(_node, _opts); end
77
+
78
+ def visit_predicate(node, opts = {})
79
+ name, rest = node
80
+ type = rest[0][1]
81
+
82
+ if name.equal?(:type?)
83
+ type = type.to_s.to_sym
84
+ return unless PREDICATE_TYPES[type]
85
+
86
+ type_definition = {
87
+ type: PREDICATE_TYPES[type],
88
+ required: opts.fetch(:required),
89
+ @config.nullable_type => opts.fetch(:nullable, false)
90
+ }
91
+
92
+ type_definition[:array] = opts[:array] if opts[:array]
93
+
94
+ keys[opts[:key]] = type_definition
95
+ elsif name.equal?(:included_in?)
96
+ type += [nil] if opts.fetch(:nullable, false)
97
+ keys[opts[:key]][:enum] = type
98
+ end
57
99
  end
58
100
 
59
- private
101
+ def visit_and(node, opts = {})
102
+ left, right = node
60
103
 
61
- def is_enum?(class_name)
62
- class_name == 'Dry::Types::Enum'
104
+ visit(left, opts)
105
+ visit(right, opts)
63
106
  end
64
107
 
65
- def is_array?(type_name)
66
- type_name == 'Array'
108
+ def visit_enum(node, opts = {})
109
+ visit(node[0], opts)
67
110
  end
68
111
 
69
- def is_primitive?(type_name)
70
- !SWAGGER_FIELD_TYPE_DEFINITIONS[type_name].nil?
112
+ def visit_sum(node, opts = {})
113
+ opts[:nullable] = true
114
+ visit(node[1], opts)
115
+ end
116
+
117
+ def visit_struct(node, opts = {})
118
+ opts[:member] = true
119
+
120
+ visit(node[1], opts)
71
121
  end
72
122
 
73
- def is_array_with_dynamic_schema?(member)
74
- member.respond_to?(:right)
123
+ def visit_array(node, opts = {})
124
+ opts[:array] = true
125
+ visit(node[0], opts)
75
126
  end
76
127
 
77
- def is_dynamic_schema?(type)
78
- !type.respond_to?(:schema)
128
+ def to_swagger
129
+ DocumentationGenerator.new(@config).generate_documentation(keys)
79
130
  end
80
131
  end
81
132
  end
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Swagger
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-swagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jane-Terziev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-31 00:00:00.000000000 Z
11
+ date: 2021-08-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A parser which converts dry-validation or dry-struct into valid swagger
14
14
  documentation
@@ -32,11 +32,16 @@ files:
32
32
  - bin/setup
33
33
  - dry-swagger.gemspec
34
34
  - lib/dry/swagger.rb
35
+ - lib/dry/swagger/config/configuration.rb
36
+ - lib/dry/swagger/config/contract_configuration.rb
37
+ - lib/dry/swagger/config/struct_configuration.rb
35
38
  - lib/dry/swagger/contract_parser.rb
39
+ - lib/dry/swagger/documentation_generator.rb
40
+ - lib/dry/swagger/errors/missing_hash_schema_error.rb
41
+ - lib/dry/swagger/errors/missing_type_error.rb
36
42
  - lib/dry/swagger/struct_parser.rb
37
43
  - lib/dry/swagger/types.rb
38
44
  - lib/dry/swagger/version.rb
39
- - lib/helpers/configuration.rb
40
45
  homepage: https://github.com/Jane-Terziev/dry-swagger
41
46
  licenses:
42
47
  - MIT
@@ -1,27 +0,0 @@
1
- module Configuration
2
-
3
- def configuration
4
- yield self
5
- end
6
-
7
- def define_setting(name, default = nil)
8
- class_variable_set("@@#{name}", default)
9
-
10
- define_class_method "#{name}=" do |value|
11
- class_variable_set("@@#{name}", value)
12
- end
13
-
14
- define_class_method name do
15
- class_variable_get("@@#{name}")
16
- end
17
- end
18
-
19
- private
20
-
21
- def define_class_method(name, &block)
22
- (class << self; self; end).instance_eval do
23
- define_method name, &block
24
- end
25
- end
26
-
27
- end