praxis 2.0.pre.8 → 2.0.pre.9
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2a11ec74ee5a52178fe7aae10c503eb61407c739c40c262386e855e1abd40ee
|
4
|
+
data.tar.gz: 71b397991329ea9726b366938de75768bedf812324e4b2d768ac7dae3f25e33e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21fbd6dbb60486cea3ec5cdd18a1b4178873ff6343182f2b44a11d71cbdac1664bec005760b77511de342fc92a63abe7864c85f10ffc1c3953734a1069f2520d
|
7
|
+
data.tar.gz: 7c0f8a5c417ad9ecd76fd67e08f1f219cc274c3bbd30ec88d7ae669e0a10d4ec5585c365ad7c3a38163b3d4f367897e7129bbbda66664947e82c37dc90213f08
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
data/lib/praxis/version.rb
CHANGED
@@ -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.
|
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-
|
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.
|
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.
|