safrano 0.4.2 → 0.4.3
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/attribute.rb +8 -7
- data/lib/odata/collection.rb +86 -34
- data/lib/odata/entity.rb +65 -69
- data/lib/odata/error.rb +18 -0
- data/lib/odata/filter/base.rb +66 -0
- data/lib/odata/filter/error.rb +27 -0
- data/lib/odata/filter/parse.rb +2 -0
- data/lib/odata/filter/sequel.rb +32 -17
- data/lib/odata/filter/sequel_function_adapter.rb +147 -0
- data/lib/odata/filter/token.rb +5 -1
- data/lib/odata/filter/tree.rb +34 -14
- data/lib/odata/navigation_attribute.rb +4 -2
- data/lib/odata/walker.rb +6 -4
- data/lib/safrano/core.rb +0 -2
- data/lib/safrano/multipart.rb +0 -9
- data/lib/safrano/odata_rack_builder.rb +0 -1
- data/lib/safrano/rack_app.rb +8 -6
- data/lib/safrano/request.rb +0 -7
- data/lib/safrano/service.rb +111 -47
- data/lib/safrano/version.rb +1 -1
- metadata +4 -2
@@ -140,9 +140,11 @@ module OData
|
|
140
140
|
[self, :end_with_value]
|
141
141
|
end
|
142
142
|
|
143
|
+
ALLOWED_TRANSITIONS = [Safrano::TransitionEnd,
|
144
|
+
Safrano::TransitionValue].freeze
|
145
|
+
|
143
146
|
def allowed_transitions
|
144
|
-
|
145
|
-
Safrano::TransitionValue]
|
147
|
+
ALLOWED_TRANSITIONS
|
146
148
|
end
|
147
149
|
end
|
148
150
|
include Transitions
|
data/lib/odata/walker.rb
CHANGED
@@ -27,9 +27,11 @@ module OData
|
|
27
27
|
|
28
28
|
# are $links requested ?
|
29
29
|
attr_reader :do_links
|
30
|
-
|
30
|
+
NIL_SERVICE_FATAL = 'Walker is called with a nil service'.freeze
|
31
|
+
EMPTYSTR = ''.freeze
|
32
|
+
SLASH = '/'.freeze
|
31
33
|
def initialize(service, path, content_id_refs = nil)
|
32
|
-
raise
|
34
|
+
raise NIL_SERVICE_FATAL unless service
|
33
35
|
|
34
36
|
path = URI.decode_www_form_component(path)
|
35
37
|
@context = service
|
@@ -51,12 +53,12 @@ module OData
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def unprefixed(prefix, path)
|
54
|
-
if (prefix ==
|
56
|
+
if (prefix == EMPTYSTR) || (prefix == SLASH)
|
55
57
|
path
|
56
58
|
else
|
57
59
|
# path.sub!(/\A#{prefix}/, '')
|
58
60
|
# TODO check
|
59
|
-
path.sub(/\A#{prefix}/,
|
61
|
+
path.sub(/\A#{prefix}/, EMPTYSTR)
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
data/lib/safrano/core.rb
CHANGED
data/lib/safrano/multipart.rb
CHANGED
@@ -15,8 +15,6 @@ module MIME
|
|
15
15
|
class Parser
|
16
16
|
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
17
17
|
|
18
|
-
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
19
|
-
|
20
18
|
attr_accessor :lines
|
21
19
|
attr_accessor :target
|
22
20
|
def initialize
|
@@ -57,7 +55,6 @@ module MIME
|
|
57
55
|
if (hmd = HMD_RGX.match(line))
|
58
56
|
@target_hd[hmd[1].downcase] = hmd[2].strip
|
59
57
|
|
60
|
-
# elsif CRLF_LINE_RGX =~ line
|
61
58
|
elsif CRLF == line
|
62
59
|
@target_ct = @target_hd[CTT_TYPE_LC] || TEXT_PLAIN
|
63
60
|
@state = new_content
|
@@ -199,7 +196,6 @@ module MIME
|
|
199
196
|
# Parser for Text::Plain
|
200
197
|
class Parser
|
201
198
|
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
202
|
-
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
203
199
|
def initialize(target)
|
204
200
|
@state = :h
|
205
201
|
@lines = []
|
@@ -213,7 +209,6 @@ module MIME
|
|
213
209
|
def parse_head(line)
|
214
210
|
if (hmd = HMD_RGX.match(line))
|
215
211
|
@target.hd[hmd[1].downcase] = hmd[2].strip
|
216
|
-
# elsif CRLF_LINE_RGX =~ line
|
217
212
|
elsif CRLF == line
|
218
213
|
@state = :b
|
219
214
|
else
|
@@ -442,7 +437,6 @@ module MIME
|
|
442
437
|
def unparse
|
443
438
|
b = "#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
|
444
439
|
@hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
|
445
|
-
# @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
|
446
440
|
b << CRLF
|
447
441
|
b << @content if @content != ''
|
448
442
|
b
|
@@ -473,7 +467,6 @@ module MIME
|
|
473
467
|
b = String.new(APPLICATION_HTTP_11)
|
474
468
|
b << "#{@status} #{StatusMessage[@status]} #{CRLF}"
|
475
469
|
@hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
|
476
|
-
# @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
|
477
470
|
b << CRLF
|
478
471
|
b << @content.join if @content
|
479
472
|
b
|
@@ -490,7 +483,6 @@ module MIME
|
|
490
483
|
# Parser for Http Media
|
491
484
|
class Parser
|
492
485
|
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
493
|
-
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
494
486
|
|
495
487
|
def initialize(target)
|
496
488
|
@state = :http
|
@@ -523,7 +515,6 @@ module MIME
|
|
523
515
|
if (hmd = HMD_RGX.match(line))
|
524
516
|
@target.content.hd[hmd[1].downcase] = hmd[2].strip
|
525
517
|
elsif CRLF == line
|
526
|
-
# elsif CRLF_LINE_RGX =~ line
|
527
518
|
@state = :b
|
528
519
|
else
|
529
520
|
@body_lines << line
|
data/lib/safrano/rack_app.rb
CHANGED
@@ -77,13 +77,13 @@ module OData
|
|
77
77
|
# the main Rack server app. Source: the Rack docu/examples and partly
|
78
78
|
# inspired from Sinatra
|
79
79
|
class ServerApp
|
80
|
-
METHODS_REGEXP = Regexp.new('HEAD|OPTIONS|GET|POST|PATCH|MERGE|PUT|DELETE')
|
80
|
+
METHODS_REGEXP = Regexp.new('HEAD|OPTIONS|GET|POST|PATCH|MERGE|PUT|DELETE').freeze
|
81
|
+
NOCACHE_HDRS = { 'Cache-Control' => 'no-cache',
|
82
|
+
'Expires' => '-1',
|
83
|
+
'Pragma' => 'no-cache' }.freeze
|
84
|
+
DATASERVICEVERSION = 'DataServiceVersion'.freeze
|
81
85
|
include MethodHandlers
|
82
86
|
def before
|
83
|
-
headers 'Cache-Control' => 'no-cache'
|
84
|
-
headers 'Expires' => '-1'
|
85
|
-
headers 'Pragma' => 'no-cache'
|
86
|
-
|
87
87
|
@request.service_base = self.class.get_service_base
|
88
88
|
|
89
89
|
neg_error = @request.negotiate_service_version
|
@@ -92,7 +92,9 @@ module OData
|
|
92
92
|
|
93
93
|
return false unless @request.service
|
94
94
|
|
95
|
-
|
95
|
+
myhdrs = NOCACHE_HDRS.dup
|
96
|
+
myhdrs[DATASERVICEVERSION] = @request.service.data_service_version
|
97
|
+
headers myhdrs
|
96
98
|
end
|
97
99
|
|
98
100
|
# dispatch for all methods requiring parsing of the path
|
data/lib/safrano/request.rb
CHANGED
@@ -108,13 +108,6 @@ module OData
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
def uribase
|
112
|
-
return @uribase if @uribase
|
113
|
-
|
114
|
-
@uribase =
|
115
|
-
"#{env['rack.url_scheme']}://#{env['HTTP_HOST']}#{service.xpath_prefix}"
|
116
|
-
end
|
117
|
-
|
118
111
|
def accept?(type)
|
119
112
|
preferred_type(type).to_s.include?(type)
|
120
113
|
end
|
data/lib/safrano/service.rb
CHANGED
@@ -2,14 +2,18 @@ require 'rexml/document'
|
|
2
2
|
require 'odata/relations.rb'
|
3
3
|
require 'odata/batch.rb'
|
4
4
|
require 'odata/error.rb'
|
5
|
+
require 'odata/filter/sequel.rb'
|
5
6
|
|
6
7
|
module OData
|
7
8
|
# this module has all methods related to expand/defered output preparation
|
8
9
|
# and will be included in Service class
|
9
10
|
module ExpandHandler
|
10
11
|
PATH_SPLITTER = %r{\A(\w+)\/?(.*)\z}.freeze
|
11
|
-
|
12
|
-
|
12
|
+
DEFERRED = '__deferred'.freeze
|
13
|
+
URI = 'uri'.freeze
|
14
|
+
|
15
|
+
def get_deferred_odata_h(entity_uri:, attrib:)
|
16
|
+
{ DEFERRED => { URI => "#{entity_uri}/#{attrib}" } }
|
13
17
|
end
|
14
18
|
|
15
19
|
# split expand argument into first and rest part
|
@@ -33,29 +37,30 @@ module OData
|
|
33
37
|
|
34
38
|
# default v2
|
35
39
|
# overriden in ServiceV1
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
RESULTS_K = 'results'.freeze
|
41
|
+
COUNT_K = '__count'.freeze
|
42
|
+
def get_coll_odata_h(array:, template:, icount: nil)
|
43
|
+
array.map! do |w|
|
44
|
+
get_entity_odata_h(entity: w, template: template)
|
41
45
|
end
|
42
46
|
if icount
|
43
|
-
{
|
47
|
+
{ RESULTS_K => array, COUNT_K => icount }
|
44
48
|
else
|
45
|
-
{
|
49
|
+
{ RESULTS_K => array }
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
49
53
|
# handle $links ... Note: $expand seems to be ignored when $links
|
50
54
|
# are requested
|
51
|
-
def get_entity_odata_link_h(entity
|
52
|
-
{ uri: entity.uri
|
55
|
+
def get_entity_odata_link_h(entity:)
|
56
|
+
{ uri: entity.uri }
|
53
57
|
end
|
54
58
|
|
55
59
|
EMPTYH = {}.freeze
|
56
|
-
|
60
|
+
METADATA_K = '__metadata'.freeze
|
61
|
+
def get_entity_odata_h(entity:, template:)
|
57
62
|
# start with metadata
|
58
|
-
hres = {
|
63
|
+
hres = { METADATA_K => entity.metadata_h }
|
59
64
|
|
60
65
|
template.each do |elmt, arg|
|
61
66
|
case elmt
|
@@ -68,10 +73,7 @@ module OData
|
|
68
73
|
arg.each do |attr, templ|
|
69
74
|
enval = entity.send(attr)
|
70
75
|
hres[attr] = if enval
|
71
|
-
|
72
|
-
get_entity_odata_h(entity: enval,
|
73
|
-
template: templ,
|
74
|
-
uribase: uribase)
|
76
|
+
get_entity_odata_h(entity: enval, template: templ)
|
75
77
|
else
|
76
78
|
# FK is NULL --> nav_value is nil --> return empty json
|
77
79
|
EMPTYH
|
@@ -82,16 +84,13 @@ module OData
|
|
82
84
|
next unless (encoll = entity.send(attr))
|
83
85
|
|
84
86
|
# nav attributes that are a collection (x..n)
|
85
|
-
hres[attr] = get_coll_odata_h(array: encoll,
|
86
|
-
template: templ,
|
87
|
-
uribase: uribase)
|
87
|
+
hres[attr] = get_coll_odata_h(array: encoll, template: templ)
|
88
88
|
# else error ?
|
89
89
|
end
|
90
90
|
when :deferr
|
91
|
+
euri = entity.uri
|
91
92
|
arg.each do |attr|
|
92
|
-
hres[attr] = get_deferred_odata_h(
|
93
|
-
attrib: attr,
|
94
|
-
uribase: uribase)
|
93
|
+
hres[attr] = get_deferred_odata_h(entity_uri: euri, attrib: attr)
|
95
94
|
end
|
96
95
|
end
|
97
96
|
end
|
@@ -144,7 +143,8 @@ module OData
|
|
144
143
|
attr_accessor :xname
|
145
144
|
attr_accessor :xnamespace
|
146
145
|
attr_accessor :xpath_prefix
|
147
|
-
|
146
|
+
attr_accessor :xserver_url
|
147
|
+
attr_accessor :uribase
|
148
148
|
attr_accessor :meta
|
149
149
|
attr_accessor :batch_handler
|
150
150
|
attr_accessor :relman
|
@@ -170,6 +170,7 @@ module OData
|
|
170
170
|
DATASERVICEVERSION_RGX = /\A([1234])(?:\.0);*\w*\z/.freeze
|
171
171
|
TRAILING_SLASH = %r{/\z}.freeze
|
172
172
|
DEFAULT_PATH_PREFIX = '/'.freeze
|
173
|
+
DEFAULT_SERVER_URL = 'http://localhost:9494'
|
173
174
|
|
174
175
|
# input is the DataServiceVersion request header string, eg.
|
175
176
|
# '2.0;blabla' ---> Version -> 2
|
@@ -212,21 +213,51 @@ module OData
|
|
212
213
|
(@v1.xpath_prefix = @xpath_prefix) if @v1
|
213
214
|
(@v2.xpath_prefix = @xpath_prefix) if @v2
|
214
215
|
end
|
216
|
+
|
217
|
+
def server_url(surl)
|
218
|
+
@xserver_url = surl.sub(TRAILING_SLASH, '')
|
219
|
+
(@v1.xserver_url = @xserver_url) if @v1
|
220
|
+
(@v2.xserver_url = @xserver_url) if @v2
|
221
|
+
end
|
222
|
+
|
215
223
|
# end public API
|
216
224
|
|
225
|
+
def set_uribase
|
226
|
+
@uribase = if @xpath_prefix.empty?
|
227
|
+
@xserver_url
|
228
|
+
else
|
229
|
+
if @xpath_prefix[0] == '/'
|
230
|
+
@xserver_url + @xpath_prefix
|
231
|
+
else
|
232
|
+
@xserver_url + '/' + @xpath_prefix
|
233
|
+
end
|
234
|
+
end
|
235
|
+
(@v1.uribase = @uribase) if @v1
|
236
|
+
(@v2.uribase = @uribase) if @v2
|
237
|
+
end
|
238
|
+
|
217
239
|
def copy_attribs_to(other)
|
218
240
|
other.cmap = @cmap
|
219
241
|
other.collections = @collections
|
242
|
+
other.allowed_transitions = @allowed_transitions
|
220
243
|
other.xtitle = @xtitle
|
221
244
|
other.xname = @xname
|
222
245
|
other.xnamespace = @xnamespace
|
223
246
|
other.xpath_prefix = @xpath_prefix
|
247
|
+
other.xserver_url = @xserver_url
|
248
|
+
other.uribase = @uribase
|
224
249
|
other.meta = ServiceMeta.new(other) # hum ... #todo: versions as well ?
|
225
250
|
other.relman = @relman
|
226
251
|
other.batch_handler = @batch_handler
|
227
252
|
other
|
228
253
|
end
|
229
254
|
|
255
|
+
# this is a central place. We extend Sequel models with OData functionality
|
256
|
+
# The included/extended modules depends on the properties(eg, pks, field types) of the model
|
257
|
+
# we differentiate
|
258
|
+
# * Single/Multi PK
|
259
|
+
# * Media/Non-Media entity
|
260
|
+
# Putting this logic here in modules loaded once on start shall result in less runtime overhead
|
230
261
|
def register_model(modelklass, entity_set_name = nil, is_media = false)
|
231
262
|
# check that the provided klass is a Sequel Model
|
232
263
|
|
@@ -313,10 +344,36 @@ module OData
|
|
313
344
|
# set default path prefix if path_prefix was not called
|
314
345
|
path_prefix(DEFAULT_PATH_PREFIX) unless @xpath_prefix
|
315
346
|
|
347
|
+
# set default server url if server_url was not called
|
348
|
+
server_url(DEFAULT_SERVER_URL) unless @xserver_url
|
349
|
+
|
350
|
+
set_uribase
|
351
|
+
|
316
352
|
@collections.each(&:finalize_publishing)
|
317
353
|
|
318
|
-
# finalize the
|
319
|
-
@collections.each { |klass|
|
354
|
+
# finalize the uri's and include NoMappingBeforeOutput or MappingBeforeOutput as needed
|
355
|
+
@collections.each { |klass|
|
356
|
+
klass.build_uri(@uribase)
|
357
|
+
if klass.time_cols.empty?
|
358
|
+
klass.include OData::NoMappingBeforeOutput
|
359
|
+
else
|
360
|
+
klass.include OData::MappingBeforeOutput
|
361
|
+
end
|
362
|
+
}
|
363
|
+
|
364
|
+
# build allowed transitions (requires that @collections are filled and sorted for having a
|
365
|
+
# correct base_url_regexp)
|
366
|
+
build_allowed_transitions
|
367
|
+
|
368
|
+
# mixin adapter specific modules where needed
|
369
|
+
case Sequel::Model.db.adapter_scheme
|
370
|
+
when :postgres
|
371
|
+
OData::Filter::FuncTree.include OData::Filter::FuncTreePostgres
|
372
|
+
when :sqlite
|
373
|
+
OData::Filter::FuncTree.include OData::Filter::FuncTreeSqlite
|
374
|
+
else
|
375
|
+
OData::Filter::FuncTree.include OData::Filter::FuncTreeDefault
|
376
|
+
end
|
320
377
|
end
|
321
378
|
|
322
379
|
def execute_deferred_iblocks
|
@@ -341,7 +398,7 @@ module OData
|
|
341
398
|
def service_xml(req)
|
342
399
|
doc = REXML::Document.new
|
343
400
|
# separator required ? ?
|
344
|
-
root = doc.add_element('service', 'xml:base' =>
|
401
|
+
root = doc.add_element('service', 'xml:base' => @uribase)
|
345
402
|
|
346
403
|
root.add_namespace('xmlns:atom', XMLNS::W3_2005_ATOM)
|
347
404
|
root.add_namespace('xmlns:app', XMLNS::W3_2007_APP)
|
@@ -452,15 +509,22 @@ module OData
|
|
452
509
|
# methods related to transitions to next state (cf. walker)
|
453
510
|
module Transitions
|
454
511
|
DOLLAR_ID_REGEXP = Regexp.new('\A\/\$').freeze
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
512
|
+
ALLOWED_TRANSITIONS_FIXED = [
|
513
|
+
Safrano::TransitionEnd,
|
514
|
+
Safrano::TransitionMetadata,
|
515
|
+
Safrano::TransitionBatch,
|
516
|
+
Safrano::TransitionContentId
|
517
|
+
].freeze
|
518
|
+
|
519
|
+
def build_allowed_transitions
|
520
|
+
@allowed_transitions = (ALLOWED_TRANSITIONS_FIXED + [
|
461
521
|
Safrano::Transition.new(%r{\A/(#{base_url_regexp})(.*)},
|
462
522
|
trans: 'transition_collection')
|
463
|
-
]
|
523
|
+
]).freeze
|
524
|
+
end
|
525
|
+
|
526
|
+
def allowed_transitions
|
527
|
+
@allowed_transitions
|
464
528
|
end
|
465
529
|
|
466
530
|
def transition_collection(match_result)
|
@@ -507,18 +571,18 @@ module OData
|
|
507
571
|
@data_service_version = '1.0'
|
508
572
|
end
|
509
573
|
|
510
|
-
def get_coll_odata_links_h(array:,
|
574
|
+
def get_coll_odata_links_h(array:, icount: nil)
|
511
575
|
array.map do |w|
|
512
|
-
get_entity_odata_link_h(entity: w
|
576
|
+
get_entity_odata_link_h(entity: w)
|
513
577
|
end
|
514
578
|
end
|
515
579
|
|
516
|
-
def get_coll_odata_h(array:, template:,
|
517
|
-
array.map do |w|
|
580
|
+
def get_coll_odata_h(array:, template:, icount: nil)
|
581
|
+
array.map! do |w|
|
518
582
|
get_entity_odata_h(entity: w,
|
519
|
-
template: template
|
520
|
-
uribase: uribase)
|
583
|
+
template: template)
|
521
584
|
end
|
585
|
+
array
|
522
586
|
end
|
523
587
|
|
524
588
|
def get_emptycoll_odata_h
|
@@ -532,19 +596,19 @@ module OData
|
|
532
596
|
@data_service_version = '2.0'
|
533
597
|
end
|
534
598
|
|
535
|
-
def get_coll_odata_links_h(array:,
|
599
|
+
def get_coll_odata_links_h(array:, icount: nil)
|
536
600
|
res = array.map do |w|
|
537
|
-
get_entity_odata_link_h(entity: w
|
601
|
+
get_entity_odata_link_h(entity: w)
|
538
602
|
end
|
539
603
|
if icount
|
540
|
-
{
|
604
|
+
{ RESULTS_K => res, COUNT_K => icount }
|
541
605
|
else
|
542
|
-
{
|
606
|
+
{ RESULTS_K => res }
|
543
607
|
end
|
544
608
|
end
|
545
609
|
|
546
610
|
def get_emptycoll_odata_h
|
547
|
-
{
|
611
|
+
{ RESULTS_K => EMPTY_HASH_IN_ARY }
|
548
612
|
end
|
549
613
|
end
|
550
614
|
|
@@ -555,9 +619,9 @@ module OData
|
|
555
619
|
@service = service
|
556
620
|
end
|
557
621
|
|
622
|
+
ALLOWED_TRANSITIONS_FIXED = [Safrano::TransitionEnd].freeze
|
558
623
|
def allowed_transitions
|
559
|
-
|
560
|
-
trans: 'transition_end')]
|
624
|
+
ALLOWED_TRANSITIONS_FIXED
|
561
625
|
end
|
562
626
|
|
563
627
|
def transition_end(_match_result)
|