safrano 0.6.1 → 0.6.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/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
|