safrano 0.3.1 → 0.3.2
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 +4 -4
- data/lib/odata/collection.rb +60 -12
- data/lib/odata/filter/parse.rb +18 -2
- data/lib/odata/filter/sequel.rb +52 -21
- data/lib/odata/filter/token.rb +11 -8
- data/lib/odata/filter/tree.rb +32 -15
- data/lib/rack_app.rb +5 -2
- data/lib/service.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz: '
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '048c3efb69194f00a6ed5593c23300ae7b9d46f7d98a546d443bd5d9c5d2c6c8'
|
4
|
+
data.tar.gz: 825698055233240072faee6e038e531884e23d938fc1ed9708e98d27cf0db3c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2697db38029b2701a418a620b74e7b8080d368cd34461073de46065d7057cb3b852cc62795cabb1bbbad82d10607bff96b899beb483a3c0f03e028cf8cb8a31
|
7
|
+
data.tar.gz: 12a8674563e67e2f3c4e475d5867f8965e0095a0ff0afea80079f6e7e59c56921abc506d33b07ca483f5f2c16024176a6e3679db26b09e979a9f9fb6fac8d755
|
data/lib/odata/collection.rb
CHANGED
@@ -41,7 +41,8 @@ module OData
|
|
41
41
|
# class methods. They Make heavy use of Sequel::Model functionality
|
42
42
|
# we will add this to our Model classes with "extend" --> self is the Class
|
43
43
|
module EntityClassBase
|
44
|
-
SINGLE_PK_URL_REGEXP = /\A\('?([\w\s]+)'?\)(.*)/.freeze
|
44
|
+
SINGLE_PK_URL_REGEXP = /\A\(\s*'?([\w\s]+)'?\s*\)(.*)/.freeze
|
45
|
+
ONLY_INTEGER_RGX = /\A[+-]?\d+\z/
|
45
46
|
|
46
47
|
attr_reader :nav_collection_url_regexp
|
47
48
|
attr_reader :nav_entity_url_regexp
|
@@ -406,6 +407,32 @@ module OData
|
|
406
407
|
%r{\A/(#{@columns.join('|')})(.*)\z}
|
407
408
|
end
|
408
409
|
|
410
|
+
# pkid can be a single value for single-pk models, or an array.
|
411
|
+
# type checking/convertion is done in check_odata_key_type
|
412
|
+
def find_by_odata_key(pkid)
|
413
|
+
# amazingly this works as expected from an Entity.get_related(...) anonymous class
|
414
|
+
# without need to redefine primary_key_lookup (returns nil for valid but unrelated keys)
|
415
|
+
primary_key_lookup(pkid)
|
416
|
+
end
|
417
|
+
|
418
|
+
# super-minimal type check, but better as nothing
|
419
|
+
def check_odata_val_type(val, type)
|
420
|
+
case type
|
421
|
+
when :integer
|
422
|
+
if (val =~ ONLY_INTEGER_RGX)
|
423
|
+
[true, Integer(val)]
|
424
|
+
else
|
425
|
+
[false, val]
|
426
|
+
end
|
427
|
+
when :string
|
428
|
+
[true, val]
|
429
|
+
else
|
430
|
+
[true, val] # todo...
|
431
|
+
end
|
432
|
+
rescue StandardError
|
433
|
+
[false, val]
|
434
|
+
end
|
435
|
+
|
409
436
|
# methods related to transitions to next state (cf. walker)
|
410
437
|
module Transitions
|
411
438
|
def transition_end(_match_result)
|
@@ -418,10 +445,17 @@ module OData
|
|
418
445
|
|
419
446
|
def transition_id(match_result)
|
420
447
|
if (id = match_result[1])
|
421
|
-
|
422
|
-
|
448
|
+
|
449
|
+
ck, casted_id = check_odata_key(id)
|
450
|
+
|
451
|
+
if ck
|
452
|
+
if (y = find_by_odata_key(casted_id))
|
453
|
+
[y, :run]
|
454
|
+
else
|
455
|
+
[nil, :error, ErrorNotFound]
|
456
|
+
end
|
423
457
|
else
|
424
|
-
[nil, :error,
|
458
|
+
[nil, :error, BadRequestError]
|
425
459
|
end
|
426
460
|
else
|
427
461
|
[nil, :error, ServerTransitionError]
|
@@ -440,24 +474,38 @@ module OData
|
|
440
474
|
# special handling for composite key
|
441
475
|
module EntityClassMultiPK
|
442
476
|
include EntityClassBase
|
443
|
-
#
|
444
|
-
|
477
|
+
# input fx='aas',fy_w='0001'
|
478
|
+
# output true, ['aas', '0001'] ... or false when typ-error
|
479
|
+
def check_odata_key(mid)
|
445
480
|
# @iuk_rgx is (needs to be) built on start with
|
446
481
|
# collklass.prepare_pk
|
447
482
|
md = @iuk_rgx.match(mid).to_a
|
448
483
|
md.shift # remove first element which is the whole match
|
449
|
-
|
450
|
-
|
451
|
-
|
484
|
+
mdc = []
|
485
|
+
error = false
|
486
|
+
primary_key.each_with_index { |pk, i|
|
487
|
+
ck, casted = check_odata_val_type(md[i], db_schema[pk][:type])
|
488
|
+
if ck
|
489
|
+
mdc << casted
|
490
|
+
else
|
491
|
+
error = true
|
492
|
+
break
|
493
|
+
end
|
494
|
+
}
|
495
|
+
if error
|
496
|
+
[false, md]
|
497
|
+
else
|
498
|
+
[true, mdc]
|
499
|
+
end
|
452
500
|
end
|
453
501
|
end
|
454
502
|
|
455
503
|
# special handling for single key
|
456
504
|
module EntityClassSinglePK
|
457
505
|
include EntityClassBase
|
458
|
-
|
459
|
-
def
|
460
|
-
|
506
|
+
|
507
|
+
def check_odata_key(id)
|
508
|
+
check_odata_val_type(id, db_schema[primary_key][:type])
|
461
509
|
end
|
462
510
|
end
|
463
511
|
end
|
data/lib/odata/filter/parse.rb
CHANGED
@@ -114,9 +114,9 @@ module OData
|
|
114
114
|
end
|
115
115
|
grow_at_cursor(unoptr)
|
116
116
|
|
117
|
-
when :
|
117
|
+
when :BinopBool
|
118
118
|
with_accepted(tok, typ) do
|
119
|
-
binoptr =
|
119
|
+
binoptr = BinopBool.new(tok)
|
120
120
|
if (prev = @binop_stack.last)
|
121
121
|
# handling of lower precedence binding vs the other
|
122
122
|
# ones(le,gt,eq...)
|
@@ -129,6 +129,22 @@ module OData
|
|
129
129
|
end
|
130
130
|
insert_before_cursor(binoptr)
|
131
131
|
end
|
132
|
+
when :BinopArithm
|
133
|
+
with_accepted(tok, typ) do
|
134
|
+
binoptr = BinopArithm.new(tok)
|
135
|
+
if (prev = @binop_stack.last)
|
136
|
+
# handling of lower precedence binding vs the other
|
137
|
+
# ones(le,gt,eq...)
|
138
|
+
unless prev.precedence < binoptr.precedence
|
139
|
+
@cursor = @binop_stack.pop
|
140
|
+
@binop_stack << binoptr
|
141
|
+
end
|
142
|
+
else
|
143
|
+
@binop_stack << binoptr
|
144
|
+
end
|
145
|
+
insert_before_cursor(binoptr)
|
146
|
+
end
|
147
|
+
|
132
148
|
when :Literal
|
133
149
|
with_accepted(tok, typ) do
|
134
150
|
@cursor.update_state(tok, typ)
|
data/lib/odata/filter/sequel.rb
CHANGED
@@ -36,27 +36,30 @@ module OData
|
|
36
36
|
Sequel.like(args[0].leuqes(jh),
|
37
37
|
args[1].leuqes_ends_like(jh))
|
38
38
|
when :substringof
|
39
|
-
|
40
|
-
|
39
|
+
|
40
|
+
# there are multiple possible argument types (but all should return edm.string)
|
41
|
+
if (args[0].is_a?(QString))
|
41
42
|
# substringof('Rhum', name) -->
|
42
43
|
# name contains substr 'Rhum'
|
43
44
|
Sequel.like(args[1].leuqes(jh),
|
44
|
-
args[0].
|
45
|
-
#
|
46
|
-
# elsif args[1].is_a? QString
|
47
|
-
## substringof(name, '__Route du Rhum__') -->
|
48
|
-
## '__Route du Rhum__' contains name as a substring
|
49
|
-
# Sequel.like(args[1].leuqes_substringof_like(dtcx),
|
50
|
-
# args[0].leuqes(dtcx) )
|
51
|
-
# or like that?
|
52
|
-
# elsif args[1].is_a? QString
|
53
|
-
# # substringof(name, 'Route du Rhum') -->
|
54
|
-
# # name contains substr 'Rhum' ??
|
55
|
-
# Sequel.like(args[0].leuqes(dtcx) ,
|
56
|
-
# args[1].leuqes_substringof_like(dtcx) )
|
45
|
+
args[0].leuqes_substringof_sig1(jh))
|
46
|
+
# special non standard (ui5 client) case ?
|
57
47
|
elsif (args[0].is_a?(Literal) && args[1].is_a?(Literal))
|
58
48
|
Sequel.like(args[1].leuqes(jh),
|
59
|
-
args[0].
|
49
|
+
args[0].leuqes_substringof_sig1(jh))
|
50
|
+
elsif (args[1].is_a?(QString))
|
51
|
+
# substringof(name, '__Route du Rhum__') -->
|
52
|
+
# '__Route du Rhum__' contains name as a substring
|
53
|
+
# TODO... check if the database supports instr (how?)
|
54
|
+
# othewise use substr(postgresql) or whatevr?
|
55
|
+
instr_substr_func = if (Sequel::Model.db.adapter_scheme == :postgres)
|
56
|
+
Sequel.function(:strpos, args[1].leuqes(jh), args[0].leuqes(jh))
|
57
|
+
else
|
58
|
+
Sequel.function(:instr, args[1].leuqes(jh), args[0].leuqes(jh))
|
59
|
+
end
|
60
|
+
|
61
|
+
Sequel::SQL::BooleanExpression.new(:>, instr_substr_func, 0)
|
62
|
+
|
60
63
|
else
|
61
64
|
# TODO... actually not supported?
|
62
65
|
raise OData::Filter::Parser::ErrorFunctionArgumentType
|
@@ -99,8 +102,8 @@ module OData
|
|
99
102
|
end
|
100
103
|
end
|
101
104
|
|
102
|
-
# Bin ops
|
103
|
-
class
|
105
|
+
# logical Bin ops
|
106
|
+
class BinopBool
|
104
107
|
def leuqes(jh)
|
105
108
|
leuqes_op = case @value
|
106
109
|
when :eq
|
@@ -129,6 +132,30 @@ module OData
|
|
129
132
|
end
|
130
133
|
end
|
131
134
|
|
135
|
+
# Arithmetic Bin ops
|
136
|
+
class BinopArithm
|
137
|
+
def leuqes(jh)
|
138
|
+
leuqes_op = case @value
|
139
|
+
when :add
|
140
|
+
:+
|
141
|
+
when :sub
|
142
|
+
:-
|
143
|
+
when :mul
|
144
|
+
:*
|
145
|
+
when :div
|
146
|
+
:/
|
147
|
+
when :mod
|
148
|
+
:%
|
149
|
+
else
|
150
|
+
raise OData::FilterParseError
|
151
|
+
end
|
152
|
+
|
153
|
+
Sequel::SQL::NumericExpression.new(leuqes_op,
|
154
|
+
@children[0].leuqes(jh),
|
155
|
+
@children[1].leuqes(jh))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
132
159
|
# Arguments or lists
|
133
160
|
class ArgTree
|
134
161
|
end
|
@@ -147,7 +174,7 @@ module OData
|
|
147
174
|
"%#{@value.to_s}"
|
148
175
|
end
|
149
176
|
|
150
|
-
def
|
177
|
+
def leuqes_substringof_sig1(_jh)
|
151
178
|
"%#{@value.to_s}%"
|
152
179
|
end
|
153
180
|
end
|
@@ -172,9 +199,13 @@ module OData
|
|
172
199
|
"%#{@value}"
|
173
200
|
end
|
174
201
|
|
175
|
-
def
|
202
|
+
def leuqes_substringof_sig1(_jh)
|
176
203
|
"%#{@value}%"
|
177
204
|
end
|
205
|
+
|
206
|
+
def as_string
|
207
|
+
@value
|
208
|
+
end
|
178
209
|
end
|
179
210
|
|
180
211
|
# Qualit (qualified lits) are words separated by /
|
@@ -200,7 +231,7 @@ module OData
|
|
200
231
|
"%#{@value}"
|
201
232
|
end
|
202
233
|
|
203
|
-
def
|
234
|
+
def leuqes_substringof_sig1(_jh)
|
204
235
|
"%#{@value}%"
|
205
236
|
end
|
206
237
|
end
|
data/lib/odata/filter/token.rb
CHANGED
@@ -8,11 +8,12 @@ module OData
|
|
8
8
|
replace substring trim toupper tolower].freeze
|
9
9
|
FUNCRGX = FUNCNAMES.join('|').freeze
|
10
10
|
QSTRINGRGX = /'((?:[^']|(?:\'{2}))*)'/.freeze
|
11
|
-
|
11
|
+
BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'.freeze
|
12
|
+
BINOARITHM = '[aA][dD][dD]|[sS][uU][bB]|[mM][uU][lL]|[dD][iI][vV]|[mM][oO][dD]'.freeze
|
12
13
|
NOTRGX = 'not|NOT'.freeze
|
13
14
|
FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?'.freeze
|
14
15
|
QUALITRGX = '\w+(?:\/\w+)+'.freeze
|
15
|
-
RGX = /(#{FUNCRGX})|([\(\),])|(#{
|
16
|
+
RGX = /(#{FUNCRGX})|([\(\),])|(#{BINOBOOL})|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
|
16
17
|
def each_typed_token(inp)
|
17
18
|
typ = nil
|
18
19
|
|
@@ -36,18 +37,20 @@ module OData
|
|
36
37
|
:Separator
|
37
38
|
end
|
38
39
|
when 2
|
39
|
-
:
|
40
|
+
:BinopBool
|
40
41
|
when 3
|
41
|
-
:
|
42
|
+
:BinopArithm
|
42
43
|
when 4
|
43
|
-
:
|
44
|
+
:UnopTree
|
44
45
|
when 5
|
45
|
-
:
|
46
|
+
:QString
|
46
47
|
when 6
|
47
|
-
:
|
48
|
+
:FPNumber
|
48
49
|
when 7
|
49
|
-
:
|
50
|
+
:Qualit
|
50
51
|
when 8
|
52
|
+
:Literal
|
53
|
+
when 9
|
51
54
|
:unmatchedQuote
|
52
55
|
end
|
53
56
|
yield found, typ
|
data/lib/odata/filter/tree.rb
CHANGED
@@ -119,7 +119,7 @@ module OData
|
|
119
119
|
|
120
120
|
def accept?(tok, typ)
|
121
121
|
case typ
|
122
|
-
when :
|
122
|
+
when :BinopBool, :BinopArithm
|
123
123
|
true
|
124
124
|
else
|
125
125
|
super(tok, typ)
|
@@ -193,6 +193,15 @@ module OData
|
|
193
193
|
super(val.downcase.to_sym)
|
194
194
|
end
|
195
195
|
|
196
|
+
def update_state(_tok, typ)
|
197
|
+
case typ
|
198
|
+
when :Literal, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm, :UnopTree, :FPNumber
|
199
|
+
@state = :closed
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class BinopBool < BinopTree
|
196
205
|
# reference:
|
197
206
|
# OData v4 par 5.1.1.9 Operator Precedence
|
198
207
|
def precedence
|
@@ -203,28 +212,36 @@ module OData
|
|
203
212
|
2
|
204
213
|
when :eq, :ne
|
205
214
|
3
|
206
|
-
when :gt, :ge, :lt, :le
|
215
|
+
when :gt, :ge, :lt, :le, :isof
|
207
216
|
4
|
208
217
|
else
|
209
218
|
999
|
210
219
|
end
|
211
220
|
end
|
212
221
|
|
213
|
-
def
|
214
|
-
|
215
|
-
when :Literal, :Qualit, :QString, :FuncTree, :BinopTree, :UnopTree, :FPNumber
|
216
|
-
@state = :closed
|
217
|
-
end
|
222
|
+
def edm_type
|
223
|
+
:bool
|
218
224
|
end
|
225
|
+
end
|
219
226
|
|
220
|
-
|
227
|
+
class BinopArithm < BinopTree
|
228
|
+
# reference:
|
229
|
+
# OData v4 par 5.1.1.9 Operator Precedence
|
230
|
+
def precedence
|
221
231
|
case @value
|
222
|
-
when :
|
223
|
-
|
232
|
+
when :add, :sub
|
233
|
+
5
|
234
|
+
when :mul, :div, :mod
|
235
|
+
6
|
224
236
|
else
|
225
|
-
|
237
|
+
999
|
226
238
|
end
|
227
239
|
end
|
240
|
+
|
241
|
+
# TODO different num types?
|
242
|
+
def edm_type
|
243
|
+
:any
|
244
|
+
end
|
228
245
|
end
|
229
246
|
|
230
247
|
# Arguments or lists
|
@@ -279,7 +296,7 @@ module OData
|
|
279
296
|
else
|
280
297
|
[false, Parser::ErrorInvalidToken.new(tok, typ, self)]
|
281
298
|
end
|
282
|
-
when :
|
299
|
+
when :BinopBool, :BinopArithm
|
283
300
|
true
|
284
301
|
else
|
285
302
|
[false, Parser::ErrorInvalidToken.new(tok, typ, self)]
|
@@ -295,7 +312,7 @@ module OData
|
|
295
312
|
class FPNumber < Leave
|
296
313
|
def accept?(tok, typ)
|
297
314
|
case typ
|
298
|
-
when :Delimiter, :Separator, :
|
315
|
+
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
299
316
|
true
|
300
317
|
else
|
301
318
|
[false, Parser::ErrorInvalidToken.new(tok, typ, self)]
|
@@ -312,7 +329,7 @@ module OData
|
|
312
329
|
class Literal < Leave
|
313
330
|
def accept?(tok, typ)
|
314
331
|
case typ
|
315
|
-
when :Delimiter, :Separator, :
|
332
|
+
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
316
333
|
true
|
317
334
|
else
|
318
335
|
[false, Parser::ErrorInvalidToken.new(tok, typ, self)]
|
@@ -353,7 +370,7 @@ module OData
|
|
353
370
|
|
354
371
|
def accept?(tok, typ)
|
355
372
|
case typ
|
356
|
-
when :Delimiter, :Separator, :
|
373
|
+
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
357
374
|
true
|
358
375
|
else
|
359
376
|
[false, Parser::ErrorInvalidToken.new(tok, typ, self)]
|
data/lib/rack_app.rb
CHANGED
@@ -145,13 +145,16 @@ module OData
|
|
145
145
|
@service_base.enable_batch
|
146
146
|
end
|
147
147
|
|
148
|
+
def self.path_prefix(path_pr)
|
149
|
+
@service_base.path_prefix path_pr
|
150
|
+
end
|
151
|
+
|
148
152
|
def self.get_service_base
|
149
153
|
@service_base
|
150
154
|
end
|
151
155
|
|
152
|
-
def self.set_servicebase(sbase
|
156
|
+
def self.set_servicebase(sbase)
|
153
157
|
@service_base = sbase
|
154
|
-
@service_base.path_prefix p_prefix
|
155
158
|
@service_base.enable_v1_service
|
156
159
|
@service_base.enable_v2_service
|
157
160
|
end
|
data/lib/service.rb
CHANGED
@@ -242,6 +242,8 @@ module OData
|
|
242
242
|
|
243
243
|
def path_prefix(path_pr)
|
244
244
|
@xpath_prefix = path_pr.sub(TRAILING_SLASH, '')
|
245
|
+
(@v1.xpath_prefix = @xpath_prefix) if @v1
|
246
|
+
(@v2.xpath_prefix = @xpath_prefix) if @v2
|
245
247
|
end
|
246
248
|
# end public API
|
247
249
|
|
@@ -251,7 +253,6 @@ module OData
|
|
251
253
|
other.xtitle = @xtitle
|
252
254
|
other.xname = @xname
|
253
255
|
other.xnamespace = @xnamespace
|
254
|
-
# other.xuribase = @xuribase
|
255
256
|
other.xpath_prefix = @xpath_prefix
|
256
257
|
other.meta = ServiceMeta.new(other) # hum ... #todo: versions as well ?
|
257
258
|
other.relman = @relman
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safrano
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- D.M.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|