praxis 2.0.pre.8 → 2.0.pre.9

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: fa1ee705af72674f99b3b697fe5199636d6268a55696e1bcdf4a892b11b7acf3
4
- data.tar.gz: e3cc19834e9640b0ee252240013367b7e444623d19e3acb9cb3eff8c83d71223
3
+ metadata.gz: f2a11ec74ee5a52178fe7aae10c503eb61407c739c40c262386e855e1abd40ee
4
+ data.tar.gz: 71b397991329ea9726b366938de75768bedf812324e4b2d768ac7dae3f25e33e
5
5
  SHA512:
6
- metadata.gz: 958b4bf7d5684477bfb371e970f8f05871c1699a4980dc508367d5d659dc013ce049b3336d55ca1b4ca1cf617ec5b10924e92e41f18102680087ebf015fe6ce7
7
- data.tar.gz: 2bbde613a765af9769b9dba0a13860b4dc520148e4263c5b4b52acf55960f8ba0aa63870f3f31c1ef73a047c33b7ffce30477d789380617f336c0893dedf31a6
6
+ metadata.gz: 21fbd6dbb60486cea3ec5cdd18a1b4178873ff6343182f2b44a11d71cbdac1664bec005760b77511de342fc92a63abe7864c85f10ffc1c3953734a1069f2520d
7
+ data.tar.gz: 7c0f8a5c417ad9ecd76fd67e08f1f219cc274c3bbd30ec88d7ae669e0a10d4ec5585c365ad7c3a38163b3d4f367897e7129bbbda66664947e82c37dc90213f08
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## next
4
4
 
5
+ ## 2.0.pre.9
6
+
7
+ - Refined OpenAPI doc generation to output only non-null attributes in the InfoObject.
8
+ - Fixed filtering params validation to properly allow null values for the "!" and "!!" operators
9
+
5
10
  ## 2.0.pre.6
6
11
 
7
12
  - Removed the explicit `links` helpers from a `MediaType`. There was too much magic and assumptions built into it. Things can still be built in a custom basis (or through a plugin) if necessary.
@@ -10,20 +10,28 @@ module Praxis
10
10
  end
11
11
 
12
12
  def dump
13
- data ={
14
- title: info.title,
15
- description: info.description,
16
- termsOfService: info.termsOfService,
17
- contact: info.contact,
18
- license: info.license,
19
- version: version,
20
- :'x-name' => info.name,
21
- :'x-logo' => {
13
+ data = { version: version }
14
+ [
15
+ :title,
16
+ :description,
17
+ :termsOfService,
18
+ :contact,
19
+ :license
20
+ ].each do |attr|
21
+ val = info.send(attr)
22
+ data[attr] = val if val
23
+ end
24
+
25
+ # Special attributes
26
+ data[:'x-name'] = info.name
27
+ if info.logo_url
28
+ data[:'x-logo'] = {
22
29
  url: info.logo_url,
23
30
  backgroundColor: "#FFFFFF",
24
31
  altText: info.title
25
32
  }
26
- }
33
+ end
34
+ data
27
35
  end
28
36
  end
29
37
  end
@@ -166,7 +166,6 @@ module Praxis
166
166
  end
167
167
 
168
168
  attr_name = match[:attribute].to_sym
169
- # TODO: we should coerce values if there's a mediatype defined?
170
169
  coerced = if media_type
171
170
  filter_components = attr_name.to_s.split('.').map(&:to_sym)
172
171
  attr, _enclosing_type = find_filter_attribute(filter_components, media_type)
@@ -217,21 +216,9 @@ module Praxis
217
216
  errors << "Operator #{item[:op]} not allowed for filter #{attr_name}"
218
217
  end
219
218
  value_type = attr_filters[:value_type]
220
- value = item[:value]
221
- if value_type && !value_type.valid_type?(value)
222
- # Allow a collection of values of the right type for multimatch (if operators are = or !=)
223
- if ['=','!='].include?(item[:op])
224
- coll_type = Attributor::Collection.of(value_type)
225
- if !coll_type.valid_type?(value)
226
- errors << "Invalid type in filter/s value for #{attr_name} " +\
227
- "(one or more of the multiple matches in #{value} are not a #{value_type.name.split('::').last})"
228
- end
229
- else
230
- errors << "Invalid type in filter value for #{attr_name} (#{value} using '#{item[:op]}' is not a #{value_type.name.split('::').last})"
231
- end
232
- end
233
-
234
219
  next unless value_type == Attributor::String
220
+
221
+ value = item[:value]
235
222
  unless value.empty?
236
223
  fuzzy_match = attr_filters[:fuzzy_match]
237
224
  if (value[-1] == '*' || value[0] == '*') && !fuzzy_match
@@ -1,3 +1,3 @@
1
1
  module Praxis
2
- VERSION = '2.0.pre.8'
2
+ VERSION = '2.0.pre.9'
3
3
  end
@@ -30,5 +30,111 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
30
30
  ])
31
31
  end
32
32
  end
33
+
34
+ context 'with an associated MediaType' do
35
+ let(:params_for_post_media_type) do
36
+ # Note wrap the filter_params (.for) type in an attribute (which then we discard), so it will
37
+ # construct it propertly by applying the block. Seems easier than creating the type alone, and
38
+ # then manually apply the block
39
+ Attributor::Attribute.new(described_class.for(Post)) do
40
+ filter 'id', using: ['=', '!=', '!']
41
+ end.type
42
+ end
43
+
44
+ context 'with a single value' do
45
+ let(:str) { 'id=1' }
46
+ it 'coerces its value to the associated mediatype attribute type' do
47
+ parsed = params_for_post_media_type.load(str).parsed_array
48
+ expect(parsed.first).to eq(:name=>:id, :op=>"=", :value=>1)
49
+ expect(Post.attributes[:id].type.valid_type?(parsed.first[:value])).to be_truthy
50
+ end
51
+ end
52
+
53
+ context 'with multimatch' do
54
+ let(:str) { 'id=1,2,3' }
55
+ it 'coerces ALL csv values to the associated mediatype attribute type' do
56
+ parsed = params_for_post_media_type.load(str).parsed_array
57
+ expect(parsed.first).to eq(:name=>:id, :op=>"=", :value=>[1, 2, 3])
58
+ parsed.first[:value].each do |val|
59
+ expect(Post.attributes[:id].type.valid_type?(val)).to be_truthy
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'with a single value that is null' do
65
+ let(:str) { 'id!' }
66
+ it 'properly loads it as null' do
67
+ parsed = params_for_post_media_type.load(str).parsed_array
68
+ expect(parsed.first).to eq(:name=>:id, :op=>"!", :value=>nil)
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ context '.validate' do
76
+ let(:filtering_params_type) do
77
+ # Note wrap the filter_params (.for) type in an attribute (which then we discard), so it will
78
+ # construct it propertly by applying the block. Seems easier than creating the type alone, and
79
+ # then manually apply the block
80
+ Attributor::Attribute.new(described_class.for(Post)) do
81
+ filter 'id', using: ['=', '!=']
82
+ filter 'title', using: ['=', '!='], fuzzy: true
83
+ filter 'content', using: ['=', '!=']
84
+ end.type
85
+ end
86
+ let(:loaded_params) { filtering_params_type.load(filters_string) }
87
+ subject { loaded_params.validate(filters_string) }
88
+
89
+ context 'errors' do
90
+ context 'given attributes that do not exist in the type' do
91
+ let(:filters_string) { 'NotAnExistingAttribute=Foobar*'}
92
+ it 'raises an error' do
93
+ expect{subject}.to raise_error(/NotAnExistingAttribute.*does not exist/)
94
+ end
95
+ end
96
+
97
+ context 'given unallowed attributes' do
98
+ let(:filters_string) { 'href=Foobar*'}
99
+ it 'raises an error' do
100
+ expect(subject).to_not be_empty
101
+ matches_error = subject.any? {|err| err =~ /Filtering by href is not allowed/}
102
+ expect(matches_error).to be_truthy
103
+ end
104
+ end
105
+
106
+ context 'given unallowed operator' do
107
+ let(:filters_string) { 'title>Foobar*'}
108
+ it 'raises an error' do
109
+ expect(subject).to_not be_empty
110
+ expect(subject.first).to match(/Operator > not allowed for filter title/)
111
+ end
112
+ end
113
+ end
114
+ context 'fuzzy matches' do
115
+ context 'when allowed' do
116
+ context 'given a fuzzy string' do
117
+ let(:filters_string) { 'title=IAmAString*'}
118
+ it 'validates properly' do
119
+ expect(subject).to be_empty
120
+ end
121
+ end
122
+ end
123
+ context 'when NOT allowed' do
124
+ context 'given a fuzzy string' do
125
+ let(:filters_string) { 'content=IAmAString*'}
126
+ it 'errors out' do
127
+ expect(subject).to_not be_empty
128
+ expect(subject.first).to match(/Fuzzy matching for content is not allowed/)
129
+ end
130
+ end
131
+ context 'given a non-fuzzy string' do
132
+ let(:filters_string) { 'content=IAmAString'}
133
+ it 'validates properly' do
134
+ expect(subject).to be_empty
135
+ end
136
+ end
137
+ end
138
+ end
33
139
  end
34
140
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: praxis
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.pre.8
4
+ version: 2.0.pre.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep M. Blanquer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-10-26 00:00:00.000000000 Z
12
+ date: 2020-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -758,7 +758,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
758
758
  - !ruby/object:Gem::Version
759
759
  version: 1.3.1
760
760
  requirements: []
761
- rubygems_version: 3.0.3
761
+ rubygems_version: 3.1.2
762
762
  signing_key:
763
763
  specification_version: 4
764
764
  summary: Building APIs the way you want it.