dry-swagger 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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