ree_lib 1.0.20 → 1.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (23) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +11 -9
  3. data/lib/ree_lib/packages/ree_dao/package/ree_dao/dataset_extensions.rb +12 -8
  4. data/lib/ree_lib/packages/ree_datetime/spec/ree_datetime/functions/find_human_time_zones_by_offset_spec.rb +0 -1
  5. data/lib/ree_lib/packages/ree_datetime/spec/ree_datetime/functions/find_time_zones_by_offset_spec.rb +0 -1
  6. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/errors/argument_error.rb +4 -0
  7. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/field.rb +9 -3
  8. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/fields_filter.rb +106 -0
  9. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/filter_fields_contract.rb +27 -0
  10. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper.rb +44 -20
  11. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper_factory.rb +1 -0
  12. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/array.rb +16 -12
  13. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper.rb +3 -0
  14. data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/type_options_spec.rb +136 -0
  15. data/lib/ree_lib/packages/ree_swagger/package/ree_swagger/functions/build_parameters.rb +1 -1
  16. data/lib/ree_lib/packages/ree_swagger/package/ree_swagger/functions/build_request_body_schema.rb +11 -3
  17. data/lib/ree_lib/packages/ree_swagger/package/ree_swagger/functions/build_serializer_schema.rb +10 -3
  18. data/lib/ree_lib/packages/ree_swagger/schemas/ree_swagger/functions/build_request_body_schema.schema.json +4 -0
  19. data/lib/ree_lib/packages/ree_swagger/schemas/ree_swagger/functions/build_serializer_schema.schema.json +4 -0
  20. data/lib/ree_lib/packages/ree_swagger/spec/functions/build_endpoint_schema_spec.rb +112 -23
  21. data/lib/ree_lib/packages/ree_swagger/spec/functions/build_serializer_schema_spec.rb +25 -1
  22. data/lib/ree_lib/version.rb +1 -1
  23. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6972cd73aa8f6a7837528ca839deab2958095a3d23ad8fdb07a0db7963354669
4
- data.tar.gz: 9439598c7328cca53fa6bfb7fe580725d55630793634d2674ad10e1a3dc96578
3
+ metadata.gz: 6a07548e9d6406a91f8b9ba8781ac7a42add43a8283225fec7147cebceaf3135
4
+ data.tar.gz: 277351000a4834065e02a5011966726d3121fac4451ec0dcfdb8d41a6ca2316c
5
5
  SHA512:
6
- metadata.gz: 720520759e9483422ca206093e0f22e8cc7a490442a7cea13eae70b77be60e31895fc2180faf916a250ddd58a1a0a3e43d7ae1bdb417c88079b6f7cddcc51355
7
- data.tar.gz: ebbea5de6b75a32c2f6c2ab7d6889258aa323121a578d575fbe77db3bf4004566b46ce747fb69a58067a5bda91246ca30df0cc00af1f19c3c74bde9fceb7fe0e
6
+ metadata.gz: 0d241902046d98abbe9affdecac2ed8d4c8ab588b078413a611b32b6b3054ae400296a96605a54ed7354a33106a5651d02bae1d6e7ef464570bac7c6cd11b897
7
+ data.tar.gz: d8f809aec0100f1727c0d8e411a6a1f192c4120b7e60dcdb4c00fea1f1503137cb368a1064aa3e74e0d25d94354090b4f2200ec1e0337074493bf08bf837a57c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ree_lib (1.0.20)
4
+ ree_lib (1.0.22)
5
5
  binding_of_caller (~> 1.0.0)
6
6
  i18n (~> 1.12.0)
7
7
  loofah (~> 2.18.0)
@@ -35,32 +35,34 @@ GEM
35
35
  loofah (2.18.0)
36
36
  crass (~> 1.0.2)
37
37
  nokogiri (>= 1.5.9)
38
- msgpack (1.5.4)
39
- nokogiri (1.13.8-x86_64-linux)
38
+ msgpack (1.6.0)
39
+ nokogiri (1.13.9-x86_64-darwin)
40
+ racc (~> 1.4)
41
+ nokogiri (1.13.9-x86_64-linux)
40
42
  racc (~> 1.4)
41
43
  oj (3.13.21)
42
- pg (1.4.3)
44
+ pg (1.4.4)
43
45
  public_suffix (5.0.0)
44
46
  racc (1.6.0)
45
47
  rainbow (3.1.1)
46
48
  rake (13.0.6)
47
- ree (1.0.5)
49
+ ree (1.0.6)
48
50
  commander (~> 4.6.0)
49
51
  rexml (3.2.5)
50
- rollbar (3.3.1)
52
+ rollbar (3.3.2)
51
53
  rspec (3.11.0)
52
54
  rspec-core (~> 3.11.0)
53
55
  rspec-expectations (~> 3.11.0)
54
56
  rspec-mocks (~> 3.11.0)
55
57
  rspec-core (3.11.0)
56
58
  rspec-support (~> 3.11.0)
57
- rspec-expectations (3.11.0)
59
+ rspec-expectations (3.11.1)
58
60
  diff-lcs (>= 1.2.0, < 2.0)
59
61
  rspec-support (~> 3.11.0)
60
- rspec-mocks (3.11.1)
62
+ rspec-mocks (3.11.2)
61
63
  diff-lcs (>= 1.2.0, < 2.0)
62
64
  rspec-support (~> 3.11.0)
63
- rspec-support (3.11.0)
65
+ rspec-support (3.11.1)
64
66
  sequel (5.58.0)
65
67
  sqlite3 (1.4.4)
66
68
  timecop (0.9.5)
@@ -111,16 +111,20 @@ module ReeDao
111
111
  end
112
112
 
113
113
  def update(entity)
114
- raw = opts[:schema_mapper].db_dump(entity)
115
- raw = extract_changes(entity, raw)
114
+ if opts[:schema_mapper]
115
+ raw = opts[:schema_mapper].db_dump(entity)
116
+ raw = extract_changes(entity, raw)
117
+
118
+ unless raw.empty?
119
+ update_persistence_state(entity, raw)
120
+ key_condition = prepare_key_condition_from_entity(entity)
121
+ where(key_condition).__original_update(raw)
122
+ end
116
123
 
117
- unless raw.empty?
118
- update_persistence_state(entity, raw)
119
- key_condition = prepare_key_condition_from_entity(entity)
120
- where(key_condition).__original_update(raw)
124
+ entity
125
+ else
126
+ __original_update(entity)
121
127
  end
122
-
123
- entity
124
128
  end
125
129
 
126
130
  def delete(entity = nil)
@@ -6,7 +6,6 @@ RSpec.describe :find_human_time_zones_by_offset do
6
6
  it {
7
7
  gmt_12 = find_human_time_zones_by_offset("+12:00")
8
8
 
9
- expect(gmt_12.length).to eq(4)
10
9
  expect(gmt_12).to be_a(Array)
11
10
  }
12
11
  end
@@ -6,7 +6,6 @@ RSpec.describe :find_time_zones_by_offset do
6
6
  it {
7
7
  gmt_12 = find_time_zones_by_offset("+12:00")
8
8
 
9
- expect(gmt_12.length).to eq(16)
10
9
  expect(gmt_12).to be_a(Array)
11
10
  }
12
11
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ReeMapper::ArgumentError < ReeMapper::Error
4
+ end
@@ -2,7 +2,8 @@
2
2
 
3
3
  class ReeMapper::Field
4
4
  attr_reader :type, :name, :from, :doc, :optional, :null, :roles, :default,
5
- :name_as_str, :name_as_instance_var_name, :from_as_str
5
+ :name_as_str, :name_as_instance_var_name, :from_as_str,
6
+ :fields_filter
6
7
 
7
8
  NO_DEFAULT = Object.new.freeze
8
9
 
@@ -15,10 +16,13 @@ class ReeMapper::Field
15
16
  optional: Bool,
16
17
  null: Bool,
17
18
  role: Nilor[ArrayOf[Symbol], Symbol],
18
- default: Any
19
+ default: Any,
20
+ only: Nilor[ReeMapper::FilterFieldsContract],
21
+ except: Nilor[ReeMapper::FilterFieldsContract]
19
22
  ] => Any
20
23
  ).throws(ArgumentError)
21
- def initialize(type, name = nil, from: nil, doc: nil, optional: false, null: false, role: nil, default: NO_DEFAULT)
24
+ def initialize(type, name = nil, from: nil, doc: nil, optional: false, null: false, role: nil, default: NO_DEFAULT,
25
+ only: nil, except: nil)
22
26
  @type = type
23
27
  @name = name
24
28
  @from = from || name
@@ -28,6 +32,8 @@ class ReeMapper::Field
28
32
  @roles = Array(role)
29
33
  @default = default
30
34
 
35
+ @fields_filter = ReeMapper::FieldsFilter.build(only: only, except: except)
36
+
31
37
  @name_as_str = @name.to_s
32
38
  @name_as_instance_var_name = :"@#{@name}"
33
39
  @from_as_str = @from.to_s
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ReeMapper::FieldsFilter
4
+ class OnlyStrategy
5
+ def initialize(only, except)
6
+ @fields = Set.new
7
+
8
+ only.each do |item|
9
+ if item.is_a? Symbol
10
+ @fields << item
11
+ else
12
+ item.each do |key, val|
13
+ @fields << key
14
+ end
15
+ end
16
+ end
17
+
18
+ if !except.nil?
19
+ except.each do |item|
20
+ if item.is_a? Symbol
21
+ @fields.delete item
22
+ else
23
+ item.each do |key, val|
24
+ @fields.delete key
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def allow?(field)
32
+ fields.include? field
33
+ end
34
+
35
+ private
36
+ attr_reader :fields
37
+ end
38
+
39
+ class ExceptStrategy
40
+ def initialize(except)
41
+ @fields = Set.new
42
+
43
+ except&.each do |item|
44
+ if item.is_a? Symbol
45
+ @fields << item
46
+ end
47
+ end
48
+ end
49
+
50
+ def allow?(field)
51
+ !fields.include?(field)
52
+ end
53
+
54
+ private
55
+ attr_reader :fields
56
+ end
57
+
58
+ class NoneStrategy
59
+ def self.allow?(field)
60
+ true
61
+ end
62
+ end
63
+
64
+ def self.empty_filter
65
+ @empty_filter ||= new(NoneStrategy, {}).freeze
66
+ end
67
+
68
+ contract Nilor[ReeMapper::FilterFieldsContract], Nilor[ReeMapper::FilterFieldsContract] => Any
69
+ def self.build(only:, except:)
70
+ return empty_filter if only.nil? && except.nil?
71
+
72
+ strategy = if !only.nil?
73
+ OnlyStrategy.new(only, except)
74
+ elsif !except.nil?
75
+ ExceptStrategy.new(except)
76
+ else
77
+ NoneStrategy
78
+ end
79
+
80
+ nested_fields_filters = {}
81
+
82
+ only = only&.select { _1.is_a? Hash }&.reduce(&:merge)
83
+ except = except&.select { _1.is_a? Hash }&.reduce(&:merge)
84
+
85
+ only&.each { nested_fields_filters[_1] = build(only: _2, except: except&.dig(_1)) }
86
+ except&.each { nested_fields_filters[_1] ||= build(only: nil, except: _2) }
87
+
88
+ new(strategy, nested_fields_filters)
89
+ end
90
+
91
+ def initialize(strategy, nested_fields_filters)
92
+ @strategy = strategy
93
+ @nested_fields_filters = nested_fields_filters
94
+ end
95
+
96
+ def allow?(field)
97
+ strategy.allow?(field)
98
+ end
99
+
100
+ def filter_for(field)
101
+ nested_fields_filters.fetch(field, self.class.empty_filter)
102
+ end
103
+
104
+ private
105
+ attr_reader :strategy, :nested_fields_filters
106
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ReeMapper::FilterFieldsContract
4
+ def self.valid?(value)
5
+ return false unless value.is_a? Array
6
+
7
+ value.each do |item|
8
+ next if item.is_a? Symbol
9
+ return false unless item.is_a? Hash
10
+
11
+ item.each do |key, val|
12
+ return false unless key.is_a?(Symbol)
13
+ return false unless valid?(val)
14
+ end
15
+ end
16
+
17
+ true
18
+ end
19
+
20
+ def self.to_s
21
+ "FilterFieldsContract"
22
+ end
23
+
24
+ def self.message(*)
25
+ "Invalid filter fields contract"
26
+ end
27
+ end
@@ -19,34 +19,58 @@ class ReeMapper::Mapper
19
19
 
20
20
  if type
21
21
  class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
22
- def #{method}(obj, name: nil, role: nil)
23
- @type.#{method}(obj, name: name, role: role)
22
+ def #{method}(obj, name: nil, role: nil, only: nil, except: nil, fields_filters: [])
23
+ if @type.is_a?(ReeMapper::Array)
24
+ @type.#{method}(obj, name: name, role: role, fields_filters: fields_filters)
25
+ else
26
+ @type.#{method}(obj, name: name, role: role)
27
+ end
24
28
  end
25
29
  RUBY
26
30
  else
27
31
  class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
28
- def #{method}(obj, name: nil, role: nil)
32
+ def #{method}(obj, name: nil, role: nil, only: nil, except: nil, fields_filters: [])
33
+ if only && !ReeMapper::FilterFieldsContract.valid?(only)
34
+ raise ReeMapper::ArgumentError, "Invalid `only` format"
35
+ end
36
+
37
+ if except && !ReeMapper::FilterFieldsContract.valid?(except)
38
+ raise ReeMapper::ArgumentError, "Invalid `except` format"
39
+ end
40
+
41
+ user_fields_filter = ReeMapper::FieldsFilter.build(only: only, except: except)
42
+
29
43
  @fields.each_with_object(@#{method}_strategy.build_object) do |(_, field), acc|
44
+ field_fields_filters = fields_filters + [user_fields_filter]
45
+
46
+ next unless field_fields_filters.all? { _1.allow? field.name }
30
47
  next unless field.has_role?(role)
31
- nested_name = name ? "\#{name}[\#{field.name_as_str}]" : field.name_as_str
32
-
33
- if @#{method}_strategy.has_value?(obj, field)
34
- value = @#{method}_strategy.get_value(obj, field)
35
- unless value.nil? && field.null
36
- value = field.type.#{method}(value, name: nested_name, role: role)
37
- end
38
- @#{method}_strategy.assign_value(acc, field, value)
39
- elsif field.optional || @#{method}_strategy.always_optional
40
- if field.has_default?
41
- value = field.default
42
- unless value.nil? && field.null
43
- value = field.type.#{method}(value, name: nested_name, role: role)
44
- end
45
- @#{method}_strategy.assign_value(acc, field, value)
46
- end
47
- else
48
+
49
+ is_with_value = @#{method}_strategy.has_value?(obj, field)
50
+ is_optional = field.optional || @#{method}_strategy.always_optional
51
+
52
+ if !is_with_value && !is_optional
48
53
  raise ReeMapper::TypeError, "Missing required field `\#{field.from_as_str}` for `\#{name || 'root'}`"
49
54
  end
55
+
56
+ next if !is_with_value && !field.has_default?
57
+
58
+ value = if is_with_value
59
+ @#{method}_strategy.get_value(obj, field)
60
+ else
61
+ field.default
62
+ end
63
+
64
+ unless value.nil? && field.null
65
+ nested_name = name ? "\#{name}[\#{field.name_as_str}]" : field.name_as_str
66
+
67
+ nested_fields_filters = field_fields_filters.map { _1.filter_for(field.name) }
68
+ nested_fields_filters += [field.fields_filter]
69
+
70
+ value = field.type.#{method}(value, name: nested_name, role: role, fields_filters: nested_fields_filters)
71
+ end
72
+
73
+ @#{method}_strategy.assign_value(acc, field, value)
50
74
  end
51
75
  end
52
76
  RUBY
@@ -76,6 +76,7 @@ class ReeMapper::MapperFactory
76
76
  raise ArgumentError, "array item can't be optional" if field_name.nil? && optional
77
77
  raise ArgumentError, 'array type should use either :each or :block' if each && blk || !each && !blk
78
78
  raise ArgumentError, 'invalid :key option value' unless HASH_KEY_OPTION_VALUES.include?(key)
79
+ raise ArgumentError, 'array does not permit :only and :except keys' if opts.key?(:only) || opts.key?(:except)
79
80
 
80
81
  if blk
81
82
  each = ReeMapper::Field.new(
@@ -8,14 +8,15 @@ class ReeMapper::Array < ReeMapper::AbstractType
8
8
  @of = of
9
9
  end
10
10
 
11
- contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Array).throws(ReeMapper::TypeError)
12
- def serialize(value, name:, role: nil)
11
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]], fields_filters: ArrayOf[ReeMapper::FieldsFilter]] => Array)
12
+ .throws(ReeMapper::TypeError)
13
+ def serialize(value, name:, role: nil, fields_filters: [])
13
14
  if value.is_a?(Array)
14
15
  value.map.with_index {
15
16
  if _1.nil? && of.null
16
17
  _1
17
18
  else
18
- of.type.serialize(_1, name: "#{name}[#{_2}]", role: role)
19
+ of.type.serialize(_1, name: "#{name}[#{_2}]", role: role, fields_filters: fields_filters + [of.fields_filter])
19
20
  end
20
21
  }
21
22
  else
@@ -23,14 +24,15 @@ class ReeMapper::Array < ReeMapper::AbstractType
23
24
  end
24
25
  end
25
26
 
26
- contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Array).throws(ReeMapper::TypeError)
27
- def cast(value, name:, role: nil)
27
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]], fields_filters: ArrayOf[ReeMapper::FieldsFilter]] => Array)
28
+ .throws(ReeMapper::TypeError)
29
+ def cast(value, name:, role: nil, fields_filters: [])
28
30
  if value.is_a?(Array)
29
31
  value.map.with_index {
30
32
  if _1.nil? && of.null
31
33
  _1
32
34
  else
33
- of.type.cast(_1, name: "#{name}[#{_2}]", role: role)
35
+ of.type.cast(_1, name: "#{name}[#{_2}]", role: role, fields_filters: fields_filters + [of.fields_filter])
34
36
  end
35
37
  }
36
38
  else
@@ -38,14 +40,15 @@ class ReeMapper::Array < ReeMapper::AbstractType
38
40
  end
39
41
  end
40
42
 
41
- contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Array).throws(ReeMapper::TypeError)
42
- def db_dump(value, name:, role: nil)
43
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]], fields_filters: ArrayOf[ReeMapper::FieldsFilter]] => Array)
44
+ .throws(ReeMapper::TypeError)
45
+ def db_dump(value, name:, role: nil, fields_filters: [])
43
46
  if value.is_a?(Array)
44
47
  value.map.with_index {
45
48
  if _1.nil? && of.null
46
49
  _1
47
50
  else
48
- of.type.db_dump(_1, name: "#{name}[#{_2}]", role: role)
51
+ of.type.db_dump(_1, name: "#{name}[#{_2}]", role: role, fields_filters: fields_filters + [of.fields_filter])
49
52
  end
50
53
  }
51
54
  else
@@ -53,14 +56,15 @@ class ReeMapper::Array < ReeMapper::AbstractType
53
56
  end
54
57
  end
55
58
 
56
- contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Array).throws(ReeMapper::TypeError)
57
- def db_load(value, name:, role: nil)
59
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]], fields_filters: ArrayOf[ReeMapper::FieldsFilter]] => Array)
60
+ .throws(ReeMapper::TypeError)
61
+ def db_load(value, name:, role: nil, fields_filters: [])
58
62
  if value.is_a?(Array)
59
63
  value.map.with_index {
60
64
  if _1.nil? && of.null
61
65
  _1
62
66
  else
63
- of.type.db_load(_1, name: "#{name}[#{_2}]", role: role)
67
+ of.type.db_load(_1, name: "#{name}[#{_2}]", role: role, fields_filters: fields_filters + [of.fields_filter])
64
68
  end
65
69
  }
66
70
  else
@@ -16,7 +16,10 @@ module ReeMapper
16
16
  require_relative 'ree_mapper/errors/coercion_error'
17
17
  require_relative 'ree_mapper/errors/type_error'
18
18
  require_relative 'ree_mapper/errors/unsupported_type_error'
19
+ require_relative 'ree_mapper/errors/argument_error'
19
20
 
21
+ require_relative 'ree_mapper/filter_fields_contract'
22
+ require_relative 'ree_mapper/fields_filter'
20
23
  require_relative 'ree_mapper/field'
21
24
 
22
25
  require_relative 'ree_mapper/types/bool'
@@ -15,6 +15,142 @@ RSpec.describe 'ReeMapper::MapperFactory type options' do
15
15
  )
16
16
  }
17
17
 
18
+ describe 'only and except' do
19
+ before {
20
+ mapper_factory.call(register_as: :point).use(:cast) {
21
+ integer :x
22
+ integer :y
23
+ integer :z
24
+ }
25
+ }
26
+
27
+ context 'with only' do
28
+ let(:mapper) {
29
+ mapper_factory.call.use(:cast) {
30
+ integer :id
31
+ integer? :opt_id
32
+ point :point, only: [:x, :y]
33
+ }
34
+ }
35
+
36
+ it {
37
+ expect(mapper.cast({ id: 1, point: { x: 1, y: 1 } })).to eq({ id: 1, point: { x: 1, y: 1 } })
38
+ }
39
+
40
+ it {
41
+ expect(mapper.cast({ opt_id: 1, point: { x: 1 } }, only: [:opt_id, point: [:x]])).to eq({ opt_id: 1, point: { x: 1 } })
42
+ }
43
+ end
44
+
45
+ context 'with except' do
46
+ let(:mapper) {
47
+ mapper_factory.call.use(:cast) {
48
+ integer :id
49
+ point :point, except: [:z]
50
+ }
51
+ }
52
+
53
+ it {
54
+ expect(mapper.cast({ id: 1, point: { x: 1, y: 1 } })).to eq({ id: 1, point: { x: 1, y: 1 } })
55
+ }
56
+
57
+ it {
58
+ expect(mapper.cast({ id: 1, point: { x: 1 } }, except: [:id, point: [:y]])).to eq({ point: { x: 1 } })
59
+ }
60
+ end
61
+
62
+ context 'with only and except' do
63
+ let(:mapper) {
64
+ mapper_factory.call.use(:cast) {
65
+ integer :included
66
+ integer :excluded
67
+ point :point, only: [:x, :y], except: [:y]
68
+
69
+ hash :matrix do
70
+ point :x, only: [:x]
71
+ point :y, except: [:x, :z]
72
+ end
73
+
74
+ array :points, each: point(only: [:x, :y])
75
+ }
76
+ }
77
+
78
+ it {
79
+ expect(mapper.cast(
80
+ {
81
+ included: 1,
82
+ excluded: 1,
83
+ point: { x: 1 },
84
+ matrix: { x: { x: 1 }, y: { y: 1 } },
85
+ points: [{ x: 1, y: 1}]
86
+ }
87
+ )).to eq(
88
+ {
89
+ included: 1,
90
+ excluded: 1,
91
+ point: { x: 1 },
92
+ matrix: { x: { x: 1 }, y: { y: 1 } },
93
+ points: [{ x: 1, y: 1 }]
94
+ }
95
+ )
96
+ }
97
+
98
+ it {
99
+ expect(mapper.cast(
100
+ {
101
+ included: 1,
102
+ excluded: 1,
103
+ point: { x: 1 },
104
+ matrix: { x: { x: 1 }, y: { y: 1 } },
105
+ points: [{ x: 1, y: 1}]
106
+ },
107
+ only: [:included, point: [:x], matrix: [x: [:x]], points: [:x]]
108
+ )).to eq(
109
+ {
110
+ included: 1,
111
+ point: { x: 1 },
112
+ matrix: { x: { x: 1 } },
113
+ points: [{ x: 1 }]
114
+ }
115
+ )
116
+ }
117
+ end
118
+
119
+ context 'with invalid only' do
120
+ let(:mapper) {
121
+ mapper_factory.call.use(:cast) {
122
+ point :point
123
+ }
124
+ }
125
+
126
+ it {
127
+ expect {
128
+ mapper.cast({}, only: {})
129
+ }.to raise_error(
130
+ ReeMapper::ArgumentError,
131
+ "Invalid `only` format"
132
+ )
133
+ }
134
+ end
135
+
136
+ context 'with invalid except' do
137
+ let(:mapper) {
138
+ mapper_factory.call.use(:cast) {
139
+ point :point
140
+ }
141
+ }
142
+
143
+ it {
144
+ expect {
145
+ mapper.cast({}, except: {})
146
+ }.to raise_error(
147
+ ReeMapper::ArgumentError,
148
+ "Invalid `except` format"
149
+ )
150
+ }
151
+ end
152
+ end
153
+
18
154
  describe 'from:' do
19
155
  let(:mapper) {
20
156
  mapper_factory.call.use(:cast).use(:serialize).use(:db_dump).use(:db_load) {
@@ -36,7 +36,7 @@ class ReeSwagger::BuildParameters
36
36
  name: field.name_as_str,
37
37
  in: is_path_param ? 'path' : 'query',
38
38
  required: is_path_param || !field.optional,
39
- schema: build_request_body_schema(field.type) || {}
39
+ schema: build_request_body_schema(field.type, [], [field.fields_filter]) || {}
40
40
  }
41
41
 
42
42
  schema[:style] = 'deepObject' if field.type.type.nil?
@@ -9,9 +9,10 @@ class ReeSwagger::BuildRequestBodySchema
9
9
 
10
10
  contract(
11
11
  ReeMapper::Mapper,
12
- ArrayOf[Symbol] => Nilor[Hash]
12
+ ArrayOf[Symbol],
13
+ ArrayOf[ReeMapper::FieldsFilter] => Nilor[Hash]
13
14
  )
14
- def call(mapper, path_params = [])
15
+ def call(mapper, path_params = [], fields_filters = [])
15
16
  if mapper.type
16
17
  return get_caster_definition(mapper.type, method(:call).to_proc)
17
18
  end
@@ -19,13 +20,20 @@ class ReeSwagger::BuildRequestBodySchema
19
20
  required_fields = []
20
21
 
21
22
  properties = mapper.fields.each_with_object({}) do |(_name, field), acc|
23
+ next unless fields_filters.all? { _1.allow?(field.name) }
24
+
22
25
  next if path_params.include?(field.name)
23
26
 
24
27
  swagger_field = {}
25
28
 
26
29
  required_fields << field.name.to_s if !field.optional
27
30
  field_mapper = field.type
28
- swagger_type = call(field_mapper)
31
+
32
+ nested_fields_filters = fields_filters.map { _1.filter_for(field.name) }
33
+ nested_fields_filters += [field.fields_filter]
34
+
35
+ swagger_type = call(field_mapper, [], nested_fields_filters)
36
+
29
37
  swagger_field.merge!(swagger_type) if swagger_type
30
38
 
31
39
  description = field.doc
@@ -7,17 +7,24 @@ class ReeSwagger::BuildSerializerSchema
7
7
  link :get_serializer_definition
8
8
  end
9
9
 
10
- contract(ReeMapper::Mapper => Nilor[Hash])
11
- def call(mapper)
10
+ contract(ReeMapper::Mapper, ArrayOf[ReeMapper::FieldsFilter] => Nilor[Hash])
11
+ def call(mapper, fields_filters = [])
12
12
  if mapper.type
13
13
  return get_serializer_definition(mapper.type, method(:call).to_proc)
14
14
  end
15
15
 
16
16
  properties = mapper.fields.each_with_object({}) do |(_name, field), acc|
17
+ next unless fields_filters.all? { _1.allow?(field.name) }
18
+
17
19
  swagger_field = {}
18
20
 
19
21
  field_mapper = field.type
20
- swagger_type = call(field_mapper)
22
+
23
+ nested_fields_filters = fields_filters.map { _1.filter_for(field.name) }
24
+ nested_fields_filters += [field.fields_filter]
25
+
26
+ swagger_type = call(field_mapper, nested_fields_filters)
27
+
21
28
  swagger_field.merge!(swagger_type) if swagger_type
22
29
 
23
30
  description = field.doc
@@ -21,6 +21,10 @@
21
21
  {
22
22
  "arg": "path_params",
23
23
  "type": "ArrayOf[Symbol]"
24
+ },
25
+ {
26
+ "arg": "fields_filters",
27
+ "type": "ArrayOf[ReeMapper::FieldsFilter]"
24
28
  }
25
29
  ]
26
30
  }
@@ -17,6 +17,10 @@
17
17
  {
18
18
  "arg": "mapper",
19
19
  "type": "ReeMapper::Mapper"
20
+ },
21
+ {
22
+ "arg": "fields_filters",
23
+ "type": "ArrayOf[ReeMapper::FieldsFilter]"
20
24
  }
21
25
  ]
22
26
  }
@@ -51,23 +51,16 @@ RSpec.describe :build_endpoint_schema do
51
51
  _tag_caster = mapper_factory.call(register_as: :tag).use(:cast) do
52
52
  string :name
53
53
  string :value
54
+ string :excluded
54
55
  end
55
56
 
56
57
  caster = mapper_factory.call.use(:cast) do
57
58
  integer :id
58
59
  string :name
59
- tag :tag
60
+ tag :tag, except: [:excluded]
60
61
  locales :locale
61
62
  end
62
63
 
63
- file_caster = mapper_factory.call.use(:cast) do
64
- integer :id
65
- end
66
-
67
- file_serializer = mapper_factory.call.use(:serialize) do
68
- string :data
69
- end
70
-
71
64
  schema = build_endpoint_schema(ReeSwagger::EndpointDto.new(
72
65
  method: :post,
73
66
  respond_to: :json,
@@ -95,20 +88,6 @@ RSpec.describe :build_endpoint_schema do
95
88
  ]
96
89
  ))
97
90
 
98
- csv_schema = build_endpoint_schema(ReeSwagger::EndpointDto.new(
99
- method: :get,
100
- respond_to: :csv,
101
- authenticate: false,
102
- path: '/files/:id',
103
- sections: ["files"],
104
- caster: file_caster,
105
- serializer: file_serializer,
106
- response_status: 200,
107
- description: "file",
108
- summary: "file summary",
109
- errors: []
110
- ))
111
-
112
91
  expect(schema).to eq(ReeSwagger::PathDto.new(
113
92
  path: '/versions/{id}',
114
93
  schema: {
@@ -174,6 +153,116 @@ RSpec.describe :build_endpoint_schema do
174
153
  }
175
154
  }
176
155
  ))
156
+ }
157
+
158
+ it {
159
+ serializer = mapper_factory.call.use(:serialize) do
160
+ integer :id
161
+ end
162
+
163
+ _tag_caster = mapper_factory.call(register_as: :tag).use(:cast) do
164
+ string :name
165
+ string :value
166
+ string :excluded
167
+ end
168
+
169
+ caster = mapper_factory.call.use(:cast) do
170
+ integer :id
171
+ string :name
172
+ tag :tag, except: [:excluded]
173
+ end
174
+
175
+ schema = build_endpoint_schema(ReeSwagger::EndpointDto.new(
176
+ method: :get,
177
+ respond_to: :json,
178
+ authenticate: false,
179
+ path: '/versions/:id',
180
+ sections: ["versions"],
181
+ caster: caster,
182
+ serializer: serializer,
183
+ response_status: 200,
184
+ description: "description",
185
+ summary: "summary",
186
+ errors: []
187
+ ))
188
+
189
+ expect(schema).to eq(ReeSwagger::PathDto.new(
190
+ path: '/versions/{id}',
191
+ schema: {
192
+ get: {
193
+ parameters: [
194
+ {
195
+ name: 'id',
196
+ in: 'path',
197
+ required: true,
198
+ schema: { type: 'integer' }
199
+ },
200
+ {
201
+ name: 'name',
202
+ in: 'query',
203
+ required: true,
204
+ schema: { type: 'string' }
205
+ },
206
+ {
207
+ name: 'tag',
208
+ in: 'query',
209
+ required: true,
210
+ style: 'deepObject',
211
+ schema: {
212
+ type: 'object',
213
+ properties: {
214
+ name: { type: 'string' },
215
+ value: { type: 'string' }
216
+ },
217
+ required: ['name', 'value']
218
+ }
219
+ }
220
+ ],
221
+ responses: {
222
+ 200 => {
223
+ description: '',
224
+ content: {
225
+ :'application/json' => {
226
+ schema: {
227
+ type: 'object',
228
+ properties: {
229
+ id: { type: 'integer' }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ }
235
+ },
236
+ summary: "summary",
237
+ description: "description",
238
+ tags: ["versions"]
239
+ }
240
+ }
241
+ ))
242
+ }
243
+
244
+ it {
245
+ file_caster = mapper_factory.call.use(:cast) do
246
+ integer :id
247
+ end
248
+
249
+ file_serializer = mapper_factory.call.use(:serialize) do
250
+ string :data
251
+ end
252
+
253
+ csv_schema = build_endpoint_schema(ReeSwagger::EndpointDto.new(
254
+ method: :get,
255
+ respond_to: :csv,
256
+ authenticate: false,
257
+ path: '/files/:id',
258
+ sections: ["files"],
259
+ caster: file_caster,
260
+ serializer: file_serializer,
261
+ response_status: 200,
262
+ description: "file",
263
+ summary: "file summary",
264
+ errors: []
265
+ ))
177
266
 
178
267
  expect(csv_schema).to eq(ReeSwagger::PathDto.new(
179
268
  path: '/files/{id}',
@@ -25,6 +25,17 @@ RSpec.describe :build_serializer_schema do
25
25
  }
26
26
 
27
27
  let(:mapper) {
28
+ nested_partial_mapper = mapper_factory.call(register_as: :nested_partial).use(:serialize) do
29
+ string :included
30
+ string :excepted
31
+ string :nested_excepted
32
+ end
33
+
34
+ partial_mapper = mapper_factory.call(register_as: :partial).use(:serialize) do
35
+ string :excepted
36
+ nested_partial :nested_partial_value, except: [:excepted]
37
+ end
38
+
28
39
  setting_mapper = mapper_factory.call(register_as: :setting).use(:serialize) do
29
40
  string :name
30
41
  string :value
@@ -55,6 +66,8 @@ RSpec.describe :build_serializer_schema do
55
66
  integer :test_null, null: true
56
67
 
57
68
  unregistered_type :test_unregistered_type
69
+
70
+ partial :partial_value, except: [:excepted, nested_partial_value: [:nested_excepted]]
58
71
  end
59
72
  }
60
73
 
@@ -102,7 +115,18 @@ RSpec.describe :build_serializer_schema do
102
115
  type: 'integer',
103
116
  nullable: true
104
117
  },
105
- test_unregistered_type: {}
118
+ test_unregistered_type: {},
119
+ partial_value: {
120
+ type: 'object',
121
+ properties: {
122
+ nested_partial_value: {
123
+ type: 'object',
124
+ properties: {
125
+ included: { type: 'string' }
126
+ }
127
+ }
128
+ }
129
+ },
106
130
  }
107
131
  }
108
132
  )
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReeLib
4
- VERSION = "1.0.20"
4
+ VERSION = "1.0.22"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ree_lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.20
4
+ version: 1.0.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruslan Gatiyatov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-28 00:00:00.000000000 Z
11
+ date: 2022-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ree
@@ -906,11 +906,14 @@ files:
906
906
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper.rb
907
907
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/default_factory.rb
908
908
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/dsl.rb
909
+ - lib/ree_lib/packages/ree_mapper/package/ree_mapper/errors/argument_error.rb
909
910
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/errors/coercion_error.rb
910
911
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/errors/error.rb
911
912
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/errors/type_error.rb
912
913
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/errors/unsupported_type_error.rb
913
914
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/field.rb
915
+ - lib/ree_lib/packages/ree_mapper/package/ree_mapper/fields_filter.rb
916
+ - lib/ree_lib/packages/ree_mapper/package/ree_mapper/filter_fields_contract.rb
914
917
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/functions/build_mapper_factory.rb
915
918
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/functions/build_mapper_strategy.rb
916
919
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper.rb