safrano 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/odata/edm/primitive_types.rb +21 -0
- data/lib/odata/entity.rb +19 -5
- data/lib/odata/filter/base.rb +4 -0
- data/lib/odata/filter/parse.rb +5 -0
- data/lib/odata/filter/token.rb +46 -46
- data/lib/odata/filter/tree.rb +30 -8
- data/lib/odata/function_import.rb +3 -0
- data/lib/odata/model_ext.rb +70 -14
- data/lib/safrano/service.rb +23 -8
- data/lib/safrano/type_mapping.rb +44 -7
- data/lib/safrano/version.rb +1 -1
- data/lib/safrano.rb +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a14d6778d6391597537f6747dc2cdd650413bd45d8351af3ba427d349c90e887
|
4
|
+
data.tar.gz: f8f899d1d1cffd48701539e85240f7368eec5f85c6d0d1647f0448f55bdfde58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbe8f715a880590d6077f392408a50082e99cfd5f23b9648fd63c64a543c361a155cba6144a63da75a4c1e48f0e2ccb1da95d19729d49cb881b82100bc122b3c
|
7
|
+
data.tar.gz: 199026ff4cf9abc042cd547232f8aa9c251c6849f9e954fe6351da2b416b1cc87d3ad974d09bbf7c12122d1bff65ca1699556bffcaae563d329984ffacb5ea53
|
@@ -25,6 +25,7 @@ module Safrano
|
|
25
25
|
# 4 55
|
26
26
|
# 5 ,2
|
27
27
|
# 6 2
|
28
|
+
DB_TYPE_GUID_RGX = /\A\s*(uuid)\s*\z/i.freeze
|
28
29
|
|
29
30
|
DB_TYPE_FLOATP_RGX = /\A\s*(FLOAT)\s*(\(\s*(\d+)\s*\))?\s*\z/i.freeze
|
30
31
|
|
@@ -96,6 +97,13 @@ module Safrano
|
|
96
97
|
end
|
97
98
|
return if metadata[:edm_type]
|
98
99
|
|
100
|
+
# try Guid with db_type:
|
101
|
+
|
102
|
+
metadata[:edm_type] = if (DB_TYPE_GUID_RGX.match(props[:db_type]))
|
103
|
+
'Edm.Guid'
|
104
|
+
end
|
105
|
+
return if metadata[:edm_type]
|
106
|
+
|
99
107
|
# try with Sequel(ruby) type
|
100
108
|
metadata[:edm_type] = case props[:type]
|
101
109
|
when :integer
|
@@ -157,6 +165,7 @@ module Safrano
|
|
157
165
|
extend OutputClassMethods
|
158
166
|
|
159
167
|
def self.convert_from_urlparam(v)
|
168
|
+
# TODO this should use base64
|
160
169
|
Contract.valid(v.dup.force_encoding('BINARY'))
|
161
170
|
end
|
162
171
|
end
|
@@ -247,6 +256,18 @@ module Safrano
|
|
247
256
|
Contract::NOK
|
248
257
|
end
|
249
258
|
end
|
259
|
+
|
260
|
+
class Guid < UUIDTools::UUID
|
261
|
+
extend OutputClassMethods
|
262
|
+
|
263
|
+
def self.convert_from_urlparam(v)
|
264
|
+
Contract::NOK unless m = Filter::Parser::Token::GUIDRGX.match(v)
|
265
|
+
|
266
|
+
Contract.valid(UUIDTools::UUID.parse m[1])
|
267
|
+
rescue StandardError
|
268
|
+
Contract::NOK
|
269
|
+
end
|
270
|
+
end
|
250
271
|
end
|
251
272
|
end
|
252
273
|
end
|
data/lib/odata/entity.rb
CHANGED
@@ -380,9 +380,20 @@ module Safrano
|
|
380
380
|
end
|
381
381
|
end
|
382
382
|
|
383
|
-
|
384
|
-
|
385
|
-
|
383
|
+
module PKUriWithFunc
|
384
|
+
def pk_uri
|
385
|
+
self.class.pk_castfunc.call(pk)
|
386
|
+
end
|
387
|
+
|
388
|
+
def media_path_id
|
389
|
+
pk_uri.to_s
|
390
|
+
end
|
391
|
+
|
392
|
+
def media_path_ids
|
393
|
+
[pk_uri]
|
394
|
+
end
|
395
|
+
end
|
396
|
+
module PKUriWithoutFunc
|
386
397
|
def pk_uri
|
387
398
|
pk
|
388
399
|
end
|
@@ -395,6 +406,11 @@ module Safrano
|
|
395
406
|
[pk]
|
396
407
|
end
|
397
408
|
end
|
409
|
+
# for a single public key
|
410
|
+
module EntitySinglePK
|
411
|
+
include Entity
|
412
|
+
# PKUriWithoutFunc or PKUriWithFunc will be included on startup
|
413
|
+
end
|
398
414
|
|
399
415
|
# for multiple key
|
400
416
|
module EntityMultiPK
|
@@ -419,7 +435,6 @@ module Safrano
|
|
419
435
|
module EntityCreateStandardOutput
|
420
436
|
# Json formatter for a create entity POST call / Standard version; return as json object
|
421
437
|
def to_odata_create_json(request:)
|
422
|
-
# TODO: Perf: reduce method call overhead
|
423
438
|
# we added this redirection for readability and flexibility
|
424
439
|
to_odata_json(request: request)
|
425
440
|
end
|
@@ -428,7 +443,6 @@ module Safrano
|
|
428
443
|
module EntityCreateArrayOutput
|
429
444
|
# Json formatter for a create entity POST call Array version
|
430
445
|
def to_odata_create_json(request:)
|
431
|
-
# TODO: Perf: reduce method call overhead
|
432
446
|
# we added this redirection for readability and flexibility
|
433
447
|
to_odata_array_json(request: request)
|
434
448
|
end
|
data/lib/odata/filter/base.rb
CHANGED
data/lib/odata/filter/parse.rb
CHANGED
@@ -174,6 +174,11 @@ module Safrano
|
|
174
174
|
@cursor.update_state(tok, typ)
|
175
175
|
grow_at_cursor(FPNumber.new(tok))
|
176
176
|
end
|
177
|
+
when :GuidLit
|
178
|
+
with_accepted(tok, typ) do
|
179
|
+
@cursor.update_state(tok, typ)
|
180
|
+
grow_at_cursor(Guid16.new(tok))
|
181
|
+
end
|
177
182
|
when :DecimalLit
|
178
183
|
with_accepted(tok, typ) do
|
179
184
|
@cursor.update_state(tok, typ)
|
data/lib/odata/filter/token.rb
CHANGED
@@ -15,65 +15,65 @@ module Safrano
|
|
15
15
|
BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'
|
16
16
|
BINOARITHM = '[aA][dD][dD]|[sS][uU][bB]|[mM][uU][lL]|[dD][iI][vV]|[mM][oO][dD]'
|
17
17
|
NOTRGX = 'not|NOT|Not'
|
18
|
-
FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?[df]?'
|
19
|
-
DECIMALRGX = '\d+(?:\.\d+)[mM]'
|
18
|
+
FPRGX = '(\d+(?:\.\d+)?(?:e[+-]?\d+)?)[df]?'
|
19
|
+
DECIMALRGX = '(\d+(?:\.\d+))[mM]'
|
20
20
|
QUALITRGX = '\w+(?:\/\w+)+'
|
21
21
|
# datetime'yyyy-mm-ddThh:mm[:ss[.fffffff]]' NOTE: Spaces are not allowed between datetime and quoted portion.
|
22
22
|
# datetime is case-insensitive
|
23
23
|
DATIRGX = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?:\:\d{2})?(?:\.\d{1,7})?'
|
24
|
-
DATETIMERGX = /datetime'#{DATIRGX}[zZ]?'/i.freeze
|
25
|
-
DATIOFFRGX = /datetimeoffset'#{DATIRGX}(?:[zZ]|[+-]\d{2}:\d{2})'
|
26
|
-
|
27
|
-
RGX = /(#{FUNCRGX})|(#{NULLRGX})|([(),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}
|
24
|
+
DATETIMERGX = /datetime'(#{DATIRGX}[zZ]?)'/i.freeze
|
25
|
+
DATIOFFRGX = /datetimeoffset'(#{DATIRGX}(?:[zZ]|[+-]\d{2}:\d{2}))'/i.freeze
|
26
|
+
GUIDRGX = /guid'([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})'/i.freeze
|
27
|
+
RGX = /(#{FUNCRGX})|(#{NULLRGX})|([(),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|#{DECIMALRGX}|#{FPRGX}|(#{QUALITRGX})|#{DATETIMERGX}|#{DATIOFFRGX}|#{GUIDRGX}|(\w+)|(')/.freeze
|
28
28
|
|
29
29
|
def each_typed_token(inp)
|
30
30
|
typ = nil
|
31
31
|
|
32
32
|
inp.scan(RGX) do |groups|
|
33
|
-
idx = nil
|
34
|
-
found = nil
|
35
33
|
groups.each_with_index do |tok, i|
|
36
|
-
if
|
37
|
-
|
34
|
+
if tok
|
35
|
+
|
36
|
+
typ = case i
|
37
|
+
when 0
|
38
|
+
:FuncTree
|
39
|
+
when 1
|
40
|
+
:NullLiteral
|
41
|
+
when 2
|
42
|
+
case tok
|
43
|
+
when '(', ')'
|
44
|
+
:Delimiter
|
45
|
+
when ','
|
46
|
+
:Separator
|
47
|
+
end
|
48
|
+
when 3
|
49
|
+
:BinopBool
|
50
|
+
when 4
|
51
|
+
:BinopArithm
|
52
|
+
when 5
|
53
|
+
:UnopTree
|
54
|
+
when 6
|
55
|
+
:QString
|
56
|
+
when 7
|
57
|
+
:DecimalLit
|
58
|
+
when 8
|
59
|
+
:FPNumber
|
60
|
+
when 9
|
61
|
+
:Qualit
|
62
|
+
when 10
|
63
|
+
:DateTimeLit
|
64
|
+
when 11
|
65
|
+
:DateTimeOffsetLit
|
66
|
+
when 12
|
67
|
+
:GuidLit
|
68
|
+
when 13
|
69
|
+
:Literal
|
70
|
+
when 14
|
71
|
+
:unmatchedQuote
|
72
|
+
end
|
73
|
+
yield tok, typ
|
38
74
|
break
|
39
75
|
end
|
40
76
|
end
|
41
|
-
typ = case idx
|
42
|
-
when 0
|
43
|
-
:FuncTree
|
44
|
-
when 1
|
45
|
-
:NullLiteral
|
46
|
-
when 2
|
47
|
-
case found
|
48
|
-
when '(', ')'
|
49
|
-
:Delimiter
|
50
|
-
when ','
|
51
|
-
:Separator
|
52
|
-
end
|
53
|
-
when 3
|
54
|
-
:BinopBool
|
55
|
-
when 4
|
56
|
-
:BinopArithm
|
57
|
-
when 5
|
58
|
-
:UnopTree
|
59
|
-
when 6
|
60
|
-
:QString
|
61
|
-
when 7
|
62
|
-
:DecimalLit
|
63
|
-
when 8
|
64
|
-
:FPNumber
|
65
|
-
when 9
|
66
|
-
:Qualit
|
67
|
-
when 10
|
68
|
-
:DateTimeLit
|
69
|
-
when 11
|
70
|
-
:DateTimeOffsetLit
|
71
|
-
when 12
|
72
|
-
:Literal
|
73
|
-
when 13
|
74
|
-
:unmatchedQuote
|
75
|
-
end
|
76
|
-
yield found, typ
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
data/lib/odata/filter/tree.rb
CHANGED
@@ -71,7 +71,7 @@ module Safrano
|
|
71
71
|
def accept?(tok, typ)
|
72
72
|
case typ
|
73
73
|
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :ArgTree,
|
74
|
-
:UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit
|
74
|
+
:UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit, :GuidLit
|
75
75
|
nil
|
76
76
|
when :Delimiter
|
77
77
|
if tok == '('
|
@@ -238,7 +238,7 @@ module Safrano
|
|
238
238
|
def update_state(_tok, typ)
|
239
239
|
case typ
|
240
240
|
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm,
|
241
|
-
:UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit
|
241
|
+
:UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit, :GuidLit
|
242
242
|
@state = :closed
|
243
243
|
end
|
244
244
|
end
|
@@ -304,7 +304,7 @@ module Safrano
|
|
304
304
|
when :Separator
|
305
305
|
@state = :sep
|
306
306
|
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber, :DecimalLit,
|
307
|
-
:DateTimeLit, :DateTimeOffsetLit
|
307
|
+
:DateTimeLit, :DateTimeOffsetLit, :GuidLit
|
308
308
|
@state = :val
|
309
309
|
end
|
310
310
|
end
|
@@ -335,7 +335,7 @@ module Safrano
|
|
335
335
|
Parser::ErrorInvalidToken.new(tok, typ, self)
|
336
336
|
end
|
337
337
|
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber, :DecimalLit,
|
338
|
-
:DateTimeLit, :DateTimeOffsetLit
|
338
|
+
:DateTimeLit, :DateTimeOffsetLit, :GuidLit
|
339
339
|
if (@state == :open) || (@state == :sep)
|
340
340
|
Parser::ErrorInvalidArity.new(tok, typ, self) if @parent.arity_full?(@children.size)
|
341
341
|
else
|
@@ -359,7 +359,9 @@ module Safrano
|
|
359
359
|
# 1.53f --> value 1.53
|
360
360
|
# 1.53d --> value 1.53
|
361
361
|
# 1.53 --> value 1.53
|
362
|
-
|
362
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
363
|
+
# Note : we dont differentiate between Float and Double here
|
364
|
+
super(val)
|
363
365
|
end
|
364
366
|
|
365
367
|
def accept?(tok, typ)
|
@@ -376,13 +378,31 @@ module Safrano
|
|
376
378
|
:number
|
377
379
|
end
|
378
380
|
end
|
381
|
+
|
382
|
+
# Edm guid
|
383
|
+
class Guid16
|
384
|
+
def accept?(tok, typ)
|
385
|
+
case typ
|
386
|
+
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
387
|
+
nil
|
388
|
+
else
|
389
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def edm_type
|
394
|
+
:guid
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
379
398
|
class DecimalLit
|
380
399
|
def initialize(val)
|
381
400
|
# 1.53m --> value 1.53
|
382
401
|
# Warning, this assumes that the m|M part in the input is really not optional
|
402
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
383
403
|
# cf. DECIMALRGX in token.rb
|
384
404
|
|
385
|
-
super(val
|
405
|
+
super(val)
|
386
406
|
end
|
387
407
|
|
388
408
|
def accept?(tok, typ)
|
@@ -447,7 +467,8 @@ module Safrano
|
|
447
467
|
class DateTimeLit
|
448
468
|
def initialize(val)
|
449
469
|
# datetime'2000-12-12T12:00:53' --> value 2000-12-12T12:00:53
|
450
|
-
|
470
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
471
|
+
super(val)
|
451
472
|
end
|
452
473
|
|
453
474
|
def accept?(tok, typ)
|
@@ -467,7 +488,8 @@ module Safrano
|
|
467
488
|
class DateTimeOffsetLit
|
468
489
|
def initialize(val)
|
469
490
|
# datetimeoffset'2000-12-12T12:00:53+02:00' --> value 2000-12-12T12:00:53+02:00
|
470
|
-
|
491
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
492
|
+
super(val)
|
471
493
|
end
|
472
494
|
|
473
495
|
def accept?(tok, typ)
|
data/lib/odata/model_ext.rb
CHANGED
@@ -48,6 +48,11 @@ module Safrano
|
|
48
48
|
# typically the block should contain the publication of the associations
|
49
49
|
attr_accessor :deferred_iblock
|
50
50
|
|
51
|
+
# allows to override standard types
|
52
|
+
attr_accessor :type_mappings
|
53
|
+
|
54
|
+
attr_accessor :pk_castfunc
|
55
|
+
|
51
56
|
# convention: entityType is the namepsaced Ruby Model class --> name is just to_s
|
52
57
|
# Warning: for handling Navigation relations, we use anonymous collection classes
|
53
58
|
# dynamically subtyped from a Model class, and in such an anonymous class
|
@@ -73,7 +78,6 @@ module Safrano
|
|
73
78
|
end
|
74
79
|
|
75
80
|
def reset
|
76
|
-
# TODO: automatically reset all attributes?
|
77
81
|
@deferred_iblock = nil
|
78
82
|
@entity_set_name = nil
|
79
83
|
@uri = nil
|
@@ -82,6 +86,8 @@ module Safrano
|
|
82
86
|
@params = nil
|
83
87
|
@cx = nil
|
84
88
|
@cols_metadata = {}
|
89
|
+
@type_mappings = {}
|
90
|
+
@pk_castfunc = nil
|
85
91
|
end
|
86
92
|
|
87
93
|
def build_uri(uribase)
|
@@ -348,6 +354,13 @@ module Safrano
|
|
348
354
|
@nav_entity_url_regexp = @nav_entity_attribs_keys.join('|')
|
349
355
|
end
|
350
356
|
|
357
|
+
# allow to override default type settings on attribute level
|
358
|
+
# for example use Edm.Guid instead of Binary(Blob) or Edm.DateTimeOffset instead of Edm.DateTime
|
359
|
+
def with_attribute(asymb, &proc)
|
360
|
+
am = AttributeTypeMapping.builder(asymb, &proc).type_mapping
|
361
|
+
@type_mappings[asymb] = am
|
362
|
+
end
|
363
|
+
|
351
364
|
EMPTYH = {}.freeze
|
352
365
|
|
353
366
|
def build_default_template
|
@@ -359,16 +372,26 @@ module Safrano
|
|
359
372
|
# cols needed catsting before final json output
|
360
373
|
@casted_cols = {}
|
361
374
|
db_schema.each do |col, props|
|
362
|
-
# first check if we have user-defined type mapping
|
375
|
+
# first check if we have user-defined regexp based global type mapping
|
363
376
|
usermap = nil
|
364
377
|
dbtyp = props[:db_type]
|
365
378
|
metadata = @cols_metadata[col]
|
366
379
|
if service.type_mappings.values.find { |map| usermap = map.match(dbtyp) }
|
367
380
|
|
368
381
|
metadata[:edm_type] = usermap.edm_type
|
382
|
+
if usermap.castfunc
|
383
|
+
@casted_cols[col] = usermap.castfunc
|
384
|
+
next # this will override our rules below !
|
385
|
+
end
|
386
|
+
end
|
369
387
|
|
370
|
-
|
371
|
-
|
388
|
+
# attribute specific type mapping
|
389
|
+
if colmap = @type_mappings[col]
|
390
|
+
metadata[:edm_type] = colmap.edm_type
|
391
|
+
if colmap.castfunc
|
392
|
+
@casted_cols[col] = colmap.castfunc
|
393
|
+
next # this will override our rules below !
|
394
|
+
end
|
372
395
|
end
|
373
396
|
|
374
397
|
if metadata[:edm_precision] && (metadata[:edm_type] =~ /\AEdm.Decimal\(/i)
|
@@ -401,14 +424,30 @@ module Safrano
|
|
401
424
|
@casted_cols[col] = ->(x) { Base64.encode64(x) unless x.nil? } # Base64
|
402
425
|
next
|
403
426
|
end
|
427
|
+
# Odata V2 Spec:
|
428
|
+
# Literal form of Edm.Guid as used in URIs formatted as a JSON string
|
429
|
+
if metadata[:edm_type] == 'Edm.Guid'
|
430
|
+
|
431
|
+
if props[:type] == :blob # Edm.Guid but as 16 byte binary Blob on DB level, eg in Sqlite
|
432
|
+
@casted_cols[col] = ->(x) {
|
433
|
+
UUIDTools::UUID.parse_raw(x).to_s unless x.nil?
|
434
|
+
} # Base64
|
435
|
+
next
|
436
|
+
end
|
437
|
+
end
|
404
438
|
# TODO: check this more in details
|
405
439
|
# NOTE: here we use :type which is the sequel defined ruby-type
|
406
440
|
if props[:type] == :datetime || props[:type] == :date
|
407
441
|
# @casted_cols[col] = ->(x) { x&.iso8601 }
|
408
442
|
@casted_cols[col] = ->(x) { x&.to_edm_json }
|
409
443
|
end
|
444
|
+
end # db_schema.each do |col, props|
|
445
|
+
|
446
|
+
# check if key needs casting. Important for later entity-uri generation !
|
447
|
+
if primary_key.is_a? Symbol # single key field
|
448
|
+
@pk_castfunc = @casted_cols[primary_key]
|
410
449
|
end
|
411
|
-
end
|
450
|
+
end # build_casted_cols(service)
|
412
451
|
|
413
452
|
def finalize_publishing(service)
|
414
453
|
build_type_name
|
@@ -417,17 +456,17 @@ module Safrano
|
|
417
456
|
build_default_template
|
418
457
|
|
419
458
|
# add edm_types into metadata store
|
420
|
-
|
459
|
+
|
421
460
|
db_schema.each do |col, props|
|
422
461
|
metadata = @cols_metadata.key?(col) ? @cols_metadata[col] : (@cols_metadata[col] = {})
|
423
462
|
Safrano.add_edm_types(metadata, props)
|
424
463
|
end
|
425
464
|
|
426
465
|
build_casted_cols(service)
|
427
|
-
|
428
|
-
#
|
429
|
-
|
430
|
-
|
466
|
+
|
467
|
+
# build pk regexps
|
468
|
+
prepare_pk
|
469
|
+
|
431
470
|
# and finally build the path lists and allowed tr's
|
432
471
|
build_attribute_path_list
|
433
472
|
build_expand_path_list
|
@@ -440,7 +479,8 @@ module Safrano
|
|
440
479
|
finalize_media if respond_to? :finalize_media
|
441
480
|
end
|
442
481
|
|
443
|
-
KEYPRED_URL_REGEXP = /\A\(\s*'?
|
482
|
+
KEYPRED_URL_REGEXP = /\A\(\s*((?:guid)?'?[\w=,\-'\s]+'?)\s*\)(.*)/.freeze
|
483
|
+
GUIDRGX = /guid'([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})'/i.freeze
|
444
484
|
def prepare_pk
|
445
485
|
if primary_key.is_a? Array
|
446
486
|
@pk_names = []
|
@@ -479,14 +519,30 @@ module Safrano
|
|
479
519
|
else
|
480
520
|
@pk_names = [primary_key.to_s]
|
481
521
|
@pk_cast_from_string = nil
|
522
|
+
|
482
523
|
kvpredicate = case db_schema[primary_key][:type]
|
483
524
|
when :integer
|
525
|
+
# TODO harmonize this with primitive_types.rb convert_from_url
|
484
526
|
@pk_cast_from_string = ->(str) { Integer(str) }
|
485
|
-
|
527
|
+
/(\d+)/.freeze
|
486
528
|
else
|
487
|
-
|
529
|
+
metadata = @cols_metadata[primary_key]
|
530
|
+
case metadata[:edm_type]
|
531
|
+
when 'Edm.Guid'
|
532
|
+
if db_schema[primary_key][:type] == :blob # Edm.Guid but as 16byte binary Blob on DB
|
533
|
+
@pk_cast_from_string = ->(str) {
|
534
|
+
# TODO harmonize this with primitive_types.rb convert_from_url
|
535
|
+
# Sequel::SQL::Blob.new([ str.gsub('-', '') ].pack('H*')) }
|
536
|
+
Sequel::SQL::Blob.new(UUIDTools::UUID.parse(str).raw)
|
537
|
+
}
|
538
|
+
end
|
539
|
+
GUIDRGX
|
540
|
+
else
|
541
|
+
/'(\w+)'/.freeze
|
542
|
+
end
|
488
543
|
end
|
489
544
|
@iuk_rgx = /\A\s*#{kvpredicate}\s*\z/
|
545
|
+
# @entity_id_url_regexp = /\A\(\s*#{kvpredicate}\s*\)(.*)/.freeze
|
490
546
|
@entity_id_url_regexp = KEYPRED_URL_REGEXP
|
491
547
|
end
|
492
548
|
end
|
@@ -663,7 +719,7 @@ module Safrano
|
|
663
719
|
end
|
664
720
|
|
665
721
|
def pk_lookup_expr(id)
|
666
|
-
id
|
722
|
+
{ primary_key => id }
|
667
723
|
end
|
668
724
|
end
|
669
725
|
|
data/lib/safrano/service.rb
CHANGED
@@ -262,19 +262,19 @@ module Safrano
|
|
262
262
|
|
263
263
|
raise(Safrano::API::ModelNameError, modelklass) unless modelklass.is_a? Sequel::Model::ClassMethods
|
264
264
|
|
265
|
-
if modelklass.
|
266
|
-
# modules were already added previously;
|
267
|
-
# cleanup state to avoid having data from previous calls
|
268
|
-
# mostly usefull for testing (eg API)
|
269
|
-
modelklass.reset
|
270
|
-
elsif modelklass.primary_key.is_a?(Array) # first API call... (normal non-testing case)
|
265
|
+
if modelklass.primary_key.is_a?(Array) # first API call... (normal non-testing case)
|
271
266
|
modelklass.extend Safrano::EntityClassMultiPK
|
272
267
|
modelklass.include Safrano::EntityMultiPK
|
273
268
|
else
|
269
|
+
|
274
270
|
modelklass.extend Safrano::EntityClassSinglePK
|
275
271
|
modelklass.include Safrano::EntitySinglePK
|
272
|
+
|
276
273
|
end
|
277
274
|
|
275
|
+
# initialize state
|
276
|
+
modelklass.reset
|
277
|
+
|
278
278
|
# Media/Non-media
|
279
279
|
if is_media
|
280
280
|
modelklass.extend Safrano::EntityClassMedia
|
@@ -289,8 +289,12 @@ module Safrano
|
|
289
289
|
modelklass.include Safrano::NonMediaEntity
|
290
290
|
end
|
291
291
|
|
292
|
-
|
292
|
+
# this needs to be done later as it can depend on overrided attribute specific Edm type
|
293
|
+
# eg. Edm-Guid. --> moved to finalize_publishing
|
294
|
+
# modelklass.prepare_pk
|
295
|
+
|
293
296
|
modelklass.prepare_fields
|
297
|
+
|
294
298
|
esname = (entity_set_name || modelklass).to_s.freeze
|
295
299
|
serv_namespace = @xnamespace
|
296
300
|
modelklass.instance_eval do
|
@@ -337,7 +341,7 @@ module Safrano
|
|
337
341
|
end
|
338
342
|
|
339
343
|
def with_db_type(*dbtypnams, &proc)
|
340
|
-
m =
|
344
|
+
m = RgxTypeMapping.builder(*dbtypnams, &proc)
|
341
345
|
@type_mappings[m.db_types_rgx] = m
|
342
346
|
end
|
343
347
|
|
@@ -405,6 +409,17 @@ module Safrano
|
|
405
409
|
vals
|
406
410
|
end
|
407
411
|
end
|
412
|
+
# needed for example if the pk is a guid, saved as binary on DB and the DB
|
413
|
+
# does not have a real uuid type but just return binary data...
|
414
|
+
# --> we need to convert the pk similarly as for json output
|
415
|
+
unless klass.primary_key.is_a?(Array) # we do this only for single PK
|
416
|
+
# guid in multi-PK not yet supported
|
417
|
+
if klass.pk_castfunc
|
418
|
+
klass.include Safrano::PKUriWithFunc
|
419
|
+
else
|
420
|
+
klass.include Safrano::PKUriWithoutFunc
|
421
|
+
end
|
422
|
+
end
|
408
423
|
end
|
409
424
|
|
410
425
|
# build allowed transitions (requires that @collections are filled and sorted for having a
|
data/lib/safrano/type_mapping.rb
CHANGED
@@ -10,8 +10,45 @@ module Safrano
|
|
10
10
|
# Base class
|
11
11
|
class TypeMapping
|
12
12
|
attr_reader :castfunc
|
13
|
-
attr_reader :db_types_rgx
|
14
13
|
attr_reader :edm_type
|
14
|
+
end
|
15
|
+
# Model attribute (column) specific mapping
|
16
|
+
class AttributeTypeMapping < TypeMapping
|
17
|
+
attr_reader :attr_name
|
18
|
+
|
19
|
+
def initialize(builder)
|
20
|
+
@edm_type = builder.xedm_type
|
21
|
+
@castfunc = builder.castfunc
|
22
|
+
end
|
23
|
+
# wrapper to handle API
|
24
|
+
class Builder
|
25
|
+
attr_reader :xedm_type
|
26
|
+
attr_reader :castfunc
|
27
|
+
attr_reader :attr_name
|
28
|
+
|
29
|
+
def initialize(atnam)
|
30
|
+
@attr_name = atnam
|
31
|
+
end
|
32
|
+
|
33
|
+
def edm_type(input)
|
34
|
+
@xedm_type = input
|
35
|
+
end
|
36
|
+
|
37
|
+
def type_mapping
|
38
|
+
AttributeTypeMapping.new(self)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.builder(atnam, &proc)
|
43
|
+
builder = Builder.new(atnam)
|
44
|
+
builder.instance_eval(&proc)
|
45
|
+
builder
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Regexp based Global mapping
|
50
|
+
class RgxTypeMapping < TypeMapping
|
51
|
+
attr_reader :db_types_rgx
|
15
52
|
|
16
53
|
# wrapper to handle API
|
17
54
|
class Builder
|
@@ -56,7 +93,7 @@ module Safrano
|
|
56
93
|
|
57
94
|
def type_mapping
|
58
95
|
# TODO: perf; return always same object when called multiple times
|
59
|
-
|
96
|
+
RgxFixedTypeMapping.new(self)
|
60
97
|
end
|
61
98
|
end # Builder
|
62
99
|
|
@@ -94,7 +131,7 @@ module Safrano
|
|
94
131
|
p1val = @md[1]
|
95
132
|
instance_exec p1val, &@proc
|
96
133
|
|
97
|
-
|
134
|
+
RgxTypeMapping1Par.new(self)
|
98
135
|
end
|
99
136
|
end
|
100
137
|
class Builder2Par < Builder
|
@@ -121,26 +158,26 @@ module Safrano
|
|
121
158
|
p1val = @md[1]
|
122
159
|
p2val = @md[2]
|
123
160
|
instance_exec p1val, p2val, &@proc
|
124
|
-
|
161
|
+
RgxTypeMapping2Par.new(self)
|
125
162
|
end
|
126
163
|
end
|
127
164
|
end
|
128
165
|
|
129
166
|
# Fixed type (ie. without variable parts)
|
130
|
-
class
|
167
|
+
class RgxFixedTypeMapping < RgxTypeMapping
|
131
168
|
def initialize(builder)
|
132
169
|
@edm_type = builder.xedm_type
|
133
170
|
@castfunc = builder.castfunc
|
134
171
|
end
|
135
172
|
end
|
136
173
|
|
137
|
-
class
|
174
|
+
class RgxTypeMapping1Par < RgxTypeMapping
|
138
175
|
def initialize(builder)
|
139
176
|
@edm_type = builder.xedm_type
|
140
177
|
@castfunc = builder.castfunc
|
141
178
|
end
|
142
179
|
end
|
143
|
-
class
|
180
|
+
class RgxTypeMapping2Par < RgxTypeMapping
|
144
181
|
def initialize(builder)
|
145
182
|
@edm_type = builder.xedm_type
|
146
183
|
@castfunc = builder.castfunc
|
data/lib/safrano/version.rb
CHANGED
data/lib/safrano.rb
CHANGED
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.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- oz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: uuidtools
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.2'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.2'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: rack-test
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|