safrano 0.3.1 → 0.3.2

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: '068df72ed0e610de674a7f8771f1ff4e5a8e1d4217c5755c08bab78e3c735588'
4
- data.tar.gz: e45fc20c09b7717c1354e80c9e7afd6b4e71a0b27cc97c99b2fd51b99abcb083
3
+ metadata.gz: '048c3efb69194f00a6ed5593c23300ae7b9d46f7d98a546d443bd5d9c5d2c6c8'
4
+ data.tar.gz: 825698055233240072faee6e038e531884e23d938fc1ed9708e98d27cf0db3c6
5
5
  SHA512:
6
- metadata.gz: 709fe03f7939265381c276192a1db812a26a55a14914de89ae220f37b99efe21ed057f05c0e071bb388eef21c01df9a8408557aca6bc19e30a2767752f7d39f4
7
- data.tar.gz: b4fb2033ec0fd0568c23762cbf660f36e879574367c61617c022847fee64747762943139eb91849f8ae762182688c7e424db5545539e1f7896e10c3e6db62113
6
+ metadata.gz: d2697db38029b2701a418a620b74e7b8080d368cd34461073de46065d7057cb3b852cc62795cabb1bbbad82d10607bff96b899beb483a3c0f03e028cf8cb8a31
7
+ data.tar.gz: 12a8674563e67e2f3c4e475d5867f8965e0095a0ff0afea80079f6e7e59c56921abc506d33b07ca483f5f2c16024176a6e3679db26b09e979a9f9fb6fac8d755
@@ -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
- if (y = find_by_odata_key(id))
422
- [y, :run]
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, ErrorNotFound]
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
- # id is for composite key, something like fx='aas',fy_w='0001'
444
- def find_by_odata_key(mid)
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
- # amazingly this works as expected from an Entity.get_related(...) anonymous class
450
- # without need to redefine primary_key_lookup (returns nil for valid but unrelated keys)
451
- primary_key_lookup(md)
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
- # id is really just the value of single pk
459
- def find_by_odata_key(id)
460
- primary_key_lookup(id)
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
@@ -114,9 +114,9 @@ module OData
114
114
  end
115
115
  grow_at_cursor(unoptr)
116
116
 
117
- when :BinopTree
117
+ when :BinopBool
118
118
  with_accepted(tok, typ) do
119
- binoptr = BinopTree.new(tok)
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)
@@ -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
- # there are two possible signatures
40
- if (args[0].is_a?(QString) && args[1].is_a?(Literal))
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].leuqes_substringof_like(jh))
45
- # according to the spec it should be like that?
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].leuqes_substringof_like(jh))
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 BinopTree < Tree
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 leuqes_substringof_like(_jh)
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 leuqes_substringof_like(_jh)
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 leuqes_substringof_like(_jh)
234
+ def leuqes_substringof_sig1(_jh)
204
235
  "%#{@value}%"
205
236
  end
206
237
  end
@@ -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
- BINOPSRGX = '[eE][qQ]|[LlgGNn][eETt]|and|AND|or|OR'.freeze
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})|([\(\),])|(#{BINOPSRGX})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
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
- :BinopTree
40
+ :BinopBool
40
41
  when 3
41
- :UnopTree
42
+ :BinopArithm
42
43
  when 4
43
- :QString
44
+ :UnopTree
44
45
  when 5
45
- :FPNumber
46
+ :QString
46
47
  when 6
47
- :Qualit
48
+ :FPNumber
48
49
  when 7
49
- :Literal
50
+ :Qualit
50
51
  when 8
52
+ :Literal
53
+ when 9
51
54
  :unmatchedQuote
52
55
  end
53
56
  yield found, typ
@@ -119,7 +119,7 @@ module OData
119
119
 
120
120
  def accept?(tok, typ)
121
121
  case typ
122
- when :BinopTree
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 update_state(_tok, typ)
214
- case typ
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
- def edm_type
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 :or, :and, :eq, :ne, :gt, :ge, :lt, :le
223
- :bool
232
+ when :add, :sub
233
+ 5
234
+ when :mul, :div, :mod
235
+ 6
224
236
  else
225
- :any
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 :BinopTree
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, :BinopTree
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, :BinopTree
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, :BinopTree
373
+ when :Delimiter, :Separator, :BinopBool, :BinopArithm
357
374
  true
358
375
  else
359
376
  [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
@@ -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, p_prefix = '')
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
@@ -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.1
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-30 00:00:00.000000000 Z
11
+ date: 2019-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack