safrano 0.3.2 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@ module Rack
7
7
  # used
8
8
  class Builder < ::Rack::Builder
9
9
  def initialize(default_app = nil, &block)
10
- super(default_app)
10
+ super(default_app) {}
11
11
  use ::Rack::Cors
12
12
  instance_eval(&block) if block_given?
13
13
  use ::Rack::Lint
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'rack'
4
- require_relative 'odata/walker.rb'
4
+ require_relative '../odata/walker.rb'
5
5
  require_relative 'request.rb'
6
6
  require_relative 'response.rb'
7
7
 
@@ -14,7 +14,7 @@ module OData
14
14
  x = if @walker.status == :end
15
15
  headers.delete('Content-Type')
16
16
  @response.headers.delete('Content-Type')
17
- [200, {}, '']
17
+ [200, EMPTY_HASH, '']
18
18
  else
19
19
  odata_error
20
20
  end
@@ -26,12 +26,7 @@ module OData
26
26
  return @walker.error.odata_get(@request) unless @walker.error.nil?
27
27
 
28
28
  # this is too critical; raise a real Exception
29
- # begin
30
29
  raise 'Walker construction failed with a unknown Error '
31
- # rescue StandardError
32
- # binding.pry
33
- # end
34
- # [500, {}, 'Server Error']
35
30
  end
36
31
 
37
32
  def odata_delete
@@ -42,6 +37,14 @@ module OData
42
37
  end
43
38
  end
44
39
 
40
+ def odata_put
41
+ if @walker.status == :end
42
+ @walker.end_context.odata_put(@request)
43
+ else
44
+ odata_error
45
+ end
46
+ end
47
+
45
48
  def odata_patch
46
49
  if @walker.status == :end
47
50
  @walker.end_context.odata_patch(@request)
@@ -67,7 +70,7 @@ module OData
67
70
  end
68
71
 
69
72
  def odata_head
70
- [200, {}, '']
73
+ [200, EMPTY_HASH, [EMPTY_STRING]]
71
74
  end
72
75
  end
73
76
 
@@ -105,7 +108,9 @@ module OData
105
108
  odata_delete
106
109
  when 'OPTIONS'
107
110
  odata_options
108
- when 'PATCH', 'PUT', 'MERGE'
111
+ when 'PUT'
112
+ odata_put
113
+ when 'PATCH', 'MERGE'
109
114
  odata_patch
110
115
  else
111
116
  raise Error
@@ -114,7 +119,7 @@ module OData
114
119
 
115
120
  def dispatch
116
121
  req_ret = if @request.request_method !~ METHODS_REGEXP
117
- [404, {}, ['Did you get lost?']]
122
+ [404, EMPTY_HASH, ['Did you get lost?']]
118
123
  elsif @request.request_method == 'HEAD'
119
124
  odata_head
120
125
  else
@@ -1,6 +1,5 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  require 'rack'
2
+ require 'rfc2047'
4
3
 
5
4
  module OData
6
5
  # monkey patch deactivate Rack/multipart because it does not work on simple
@@ -10,6 +9,7 @@ module OData
10
9
  HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze
11
10
  HEADER_VAL_RAW = '(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*'.freeze
12
11
  HEADER_VAL_WITH_PAR = /(?:#{HEADER_VAL_RAW})\s*(?:;#{HEADER_PARAM})*/.freeze
12
+ ON_CGST_ERROR = (proc { |r| raise(Sequel::Rollback) if r.in_changeset })
13
13
 
14
14
  # borowed from Sinatra
15
15
  class AcceptEntry
@@ -94,7 +94,7 @@ module OData
94
94
  end
95
95
 
96
96
  def create_odata_walker
97
- @walker = Walker.new(@service, path_info, @content_id_references)
97
+ @env['safrano.walker'] = @walker = Walker.new(@service, path_info, @content_id_references)
98
98
  end
99
99
 
100
100
  def accept
@@ -132,22 +132,35 @@ module OData
132
132
  end
133
133
  end
134
134
 
135
- def with_parsed_data(on_error: nil)
135
+ def with_media_data
136
+ if (filename = @env['HTTP_SLUG'])
137
+
138
+ yield @env['rack.input'],
139
+ content_type.split(';').first,
140
+ Rfc2047.decode(filename)
141
+
142
+ else
143
+ ON_CGST_ERROR.call(self)
144
+ [400, EMPTY_HASH, ['File upload error: Missing SLUG']]
145
+ end
146
+ end
147
+
148
+ def with_parsed_data
136
149
  if content_type == APPJSON
137
150
  # Parse json payload
138
151
  begin
139
152
  data = JSON.parse(body.read)
140
153
  rescue JSON::ParserError => e
141
- on_error.call if on_error
142
- return [400, {}, ['JSON Parser Error while parsing payload : ',
143
- e.message]]
154
+ ON_CGST_ERROR.call(self)
155
+ return [400, EMPTY_HASH, ['JSON Parser Error while parsing payload : ',
156
+ e.message]]
144
157
  end
145
158
 
146
159
  yield data
147
160
 
148
161
  else # TODO: other formats
149
162
 
150
- [415, {}, []]
163
+ [415, EMPTY_HASH, EMPTY_ARRAY]
151
164
  end
152
165
  end
153
166
 
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env ruby
2
1
  require 'rack'
3
2
 
4
3
  # monkey patch deactivate Rack/multipart because it does not work on simple
@@ -35,7 +34,7 @@ module OData
35
34
 
36
35
  if drop_body?
37
36
  close
38
- result = []
37
+ result = EMPTY_ARRAY
39
38
  end
40
39
 
41
40
  if calculate_content_length?
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require_relative './sequel/plugins/join_by_paths.rb'
3
+ require_relative '../sequel/plugins/join_by_paths.rb'
4
4
 
5
5
  Sequel::Model.plugin Sequel::Plugins::JoinByPaths
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  require 'rexml/document'
4
2
  require 'odata/relations.rb'
5
3
  require 'odata/batch.rb'
@@ -23,20 +21,22 @@ module OData
23
21
  # TODO: check errorhandling
24
22
  raise OData::ServerError if cur_exp.nil?
25
23
 
26
- k = cur_exp.to_sym
24
+ k_s = cur_exp
25
+
27
26
  else
28
- k = exp_one.strip.to_sym
27
+ k_s = exp_one.strip
29
28
  rest_exp = nil
30
29
  end
31
- yield k, rest_exp
30
+ k = k_s.to_sym
31
+ yield k, k_s, rest_exp
32
32
  end
33
33
 
34
34
  # default v2
35
35
  # overriden in ServiceV1
36
- def get_coll_odata_h(array:, expand: nil, uribase:, icount: nil)
36
+ def get_coll_odata_h(array:, template:, uribase:, icount: nil)
37
37
  res = array.map do |w|
38
38
  get_entity_odata_h(entity: w,
39
- expand: expand,
39
+ template: template,
40
40
  uribase: uribase)
41
41
  end
42
42
  if icount
@@ -46,89 +46,55 @@ module OData
46
46
  end
47
47
  end
48
48
 
49
- # handle a single expand
50
- def handle_entity_expand_one(entity:, exp_one:, nav_values_h:, nav_coll_h:,
51
- uribase:)
52
-
53
- split_entity_expand_arg(exp_one) do |first, rest_exp|
54
- if (enval = entity.nav_values[first])
55
- nav_values_h[first.to_s] = get_entity_odata_h(entity: enval,
56
- expand: rest_exp,
57
- uribase: uribase)
58
- elsif (encoll = entity.nav_coll[first])
59
- # nav attributes that are a collection (x..n)
60
- nav_coll_h[first.to_s] = get_coll_odata_h(array: encoll,
61
- expand: rest_exp,
62
- uribase: uribase)
63
-
64
- end
65
- end
66
- end
67
-
68
- def handle_entity_expand(entity:, expand:, nav_values_h:,
69
- nav_coll_h:, uribase:)
70
- expand.strip!
71
- explist = expand.split(',')
72
- # handle multiple expands
73
- explist.each do |exp|
74
- handle_entity_expand_one(entity: entity, exp_one: exp,
75
- nav_values_h: nav_values_h,
76
- nav_coll_h: nav_coll_h,
77
- uribase: uribase)
78
- end
79
- end
80
-
81
- def handle_entity_deferred_attribs(entity:, nav_values_h:,
82
- nav_coll_h:, uribase:)
83
- entity.nav_values.each_key do |ksy|
84
- ks = ksy.to_s
85
- next if nav_values_h.key?(ks)
86
-
87
- nav_values_h[ks] = get_deferred_odata_h(entity: entity,
88
- attrib: ks, uribase: uribase)
89
- end
90
- entity.nav_coll.each_key do |ksy|
91
- ks = ksy.to_s
92
- next if nav_coll_h.key?(ks)
93
-
94
- nav_coll_h[ks] = get_deferred_odata_h(entity: entity, attrib: ks,
95
- uribase: uribase)
96
- end
97
- end
98
-
99
49
  # handle $links ... Note: $expand seems to be ignored when $links
100
50
  # are requested
101
51
  def get_entity_odata_link_h(entity:, uribase:)
102
52
  { uri: entity.uri(uribase) }
103
53
  end
104
54
 
105
- def get_entity_odata_h(entity:, expand: nil, uribase:)
106
- hres = {}
107
- hres['__metadata'] = entity.metadata_h(uribase: uribase)
55
+ EMPTYH = {}.freeze
56
+ def get_entity_odata_h(entity:, template:, uribase:)
57
+ # start with metadata
58
+ hres = { '__metadata' => entity.metadata_h(uribase: uribase) }
108
59
 
109
- # hres.merge!(entity.values)
110
- hres.merge!(entity.casted_values)
60
+ template.each do |elmt, arg|
61
+ case elmt
62
+ when :all_values
63
+ hres.merge! entity.casted_values
64
+ when :selected_vals
65
+ hres.merge! entity.casted_values(arg)
66
+ when :expand_e
111
67
 
112
- nav_values_h = {}
113
- nav_coll_h = {}
68
+ arg.each do |attr, templ|
69
+ enval = entity.send(attr)
70
+ hres[attr] = if enval
114
71
 
115
- # handle expanded nav attributes
116
- unless expand.nil?
117
- handle_entity_expand(entity: entity, expand: expand,
118
- nav_values_h: nav_values_h,
119
- nav_coll_h: nav_coll_h,
120
- uribase: uribase)
72
+ get_entity_odata_h(entity: enval,
73
+ template: templ,
74
+ uribase: uribase)
75
+ else
76
+ # FK is NULL --> nav_value is nil --> return empty json
77
+ EMPTYH
78
+ end
79
+ end
80
+ when :expand_c
81
+ arg.each do |attr, templ|
82
+ next unless (encoll = entity.send(attr))
83
+
84
+ # nav attributes that are a collection (x..n)
85
+ hres[attr] = get_coll_odata_h(array: encoll,
86
+ template: templ,
87
+ uribase: uribase)
88
+ # else error ?
89
+ end
90
+ when :deferr
91
+ arg.each do |attr|
92
+ hres[attr] = get_deferred_odata_h(entity: entity,
93
+ attrib: attr,
94
+ uribase: uribase)
95
+ end
96
+ end
121
97
  end
122
-
123
- # handle not expanded (deferred) nav attributes
124
- handle_entity_deferred_attribs(entity: entity,
125
- nav_values_h: nav_values_h,
126
- nav_coll_h: nav_coll_h,
127
- uribase: uribase)
128
- # merge ...
129
- hres.merge!(nav_values_h)
130
- hres.merge!(nav_coll_h)
131
-
132
98
  hres
133
99
  end
134
100
  end
@@ -203,6 +169,7 @@ module OData
203
169
 
204
170
  DATASERVICEVERSION_RGX = /\A([1234])(?:\.0);*\w*\z/.freeze
205
171
  TRAILING_SLASH = %r{/\z}.freeze
172
+ DEFAULT_PATH_PREFIX = '/'.freeze
206
173
 
207
174
  # input is the DataServiceVersion request header string, eg.
208
175
  # '2.0;blabla' ---> Version -> 2
@@ -260,19 +227,36 @@ module OData
260
227
  other
261
228
  end
262
229
 
263
- def register_model(modelklass, entity_set_name = nil)
230
+ def register_model(modelklass, entity_set_name = nil, is_media = false)
264
231
  # check that the provided klass is a Sequel Model
265
- unless modelklass.is_a? Sequel::Model::ClassMethods
266
- raise OData::API::ModelNameError, modelklass
267
- end
268
232
 
269
- if modelklass.primary_key.is_a?(Array)
233
+ raise(OData::API::ModelNameError, modelklass) unless modelklass.is_a? Sequel::Model::ClassMethods
234
+
235
+ if modelklass.ancestors.include? OData::Entity
236
+ # modules were already added previously;
237
+ # cleanup state to avoid having data from previous calls
238
+ # mostly usefull for testing (eg API)
239
+ modelklass.reset
240
+ elsif modelklass.primary_key.is_a?(Array) # first API call... (normal non-testing case)
270
241
  modelklass.extend OData::EntityClassMultiPK
271
242
  modelklass.include OData::EntityMultiPK
272
243
  else
273
244
  modelklass.extend OData::EntityClassSinglePK
274
245
  modelklass.include OData::EntitySinglePK
275
246
  end
247
+ # Media/Non-media
248
+ if is_media
249
+ modelklass.extend OData::EntityClassMedia
250
+ # set default media handler . Can be overridden later with the
251
+ # "use HandlerKlass, options" API
252
+
253
+ modelklass.set_default_media_handler
254
+ modelklass.api_check_media_fields
255
+ modelklass.include OData::MediaEntity
256
+ else
257
+ modelklass.extend OData::EntityClassNonMedia
258
+ modelklass.include OData::NonMediaEntity
259
+ end
276
260
 
277
261
  modelklass.prepare_pk
278
262
  modelklass.prepare_fields
@@ -290,6 +274,14 @@ module OData
290
274
  modelklass.deferred_iblock = block if block_given?
291
275
  end
292
276
 
277
+ def publish_media_model(modelklass, entity_set_name = nil, &block)
278
+ register_model(modelklass, entity_set_name, true)
279
+ # we need to execute the passed block in a deferred step
280
+ # after all models have been registered (due to rel. dependancies)
281
+ # modelklass.instance_eval(&block) if block_given?
282
+ modelklass.deferred_iblock = block if block_given?
283
+ end
284
+
293
285
  def cmap=(imap)
294
286
  @cmap = imap
295
287
  set_collections_sorted(@cmap.values)
@@ -300,15 +292,14 @@ module OData
300
292
  # example: CrewMember must be matched before Crew otherwise we get error
301
293
  def set_collections_sorted(coll_data)
302
294
  @collections = coll_data
303
- if @collections
304
- @collections.sort_by! { |klass| klass.entity_set_name.size }.reverse!
305
- end
295
+ @collections.sort_by! { |klass| klass.entity_set_name.size }.reverse! if @collections
306
296
  @collections
307
297
  end
308
298
 
309
299
  # to be called at end of publishing block to ensure we get the right names
310
300
  # and additionally build the list of valid attribute path's used
311
301
  # for validation of $orderby or $filter params
302
+
312
303
  def finalize_publishing
313
304
  # build the cmap
314
305
  @cmap = {}
@@ -319,8 +310,13 @@ module OData
319
310
  # now that we know all model klasses we can handle relationships
320
311
  execute_deferred_iblocks
321
312
 
322
- # and finally build the path list
323
- @collections.each(&:build_attribute_path_list)
313
+ # set default path prefix if path_prefix was not called
314
+ path_prefix(DEFAULT_PATH_PREFIX) unless @xpath_prefix
315
+
316
+ @collections.each(&:finalize_publishing)
317
+
318
+ # finalize the media handlers
319
+ @collections.each { |klass| }
324
320
  end
325
321
 
326
322
  def execute_deferred_iblocks
@@ -368,7 +364,7 @@ module OData
368
364
  def add_metadata_xml_entity_type(schema)
369
365
  @collections.each do |klass|
370
366
  enty = klass.add_metadata_rexml(schema)
371
- klass.add_metadata_navs_rexml(enty, @cmap, @relman, @xnamespace)
367
+ klass.add_metadata_navs_rexml(enty, @relman, @xnamespace)
372
368
  end
373
369
  end
374
370
 
@@ -497,7 +493,7 @@ module OData
497
493
  # Documents MUST be
498
494
  # identified with the "application/atomsvc+xml" media type (see
499
495
  # [RFC5023] section 8).
500
- [200, CT_ATOMXML, service_xml(req)]
496
+ [200, CT_ATOMXML, [service_xml(req)]]
501
497
  else
502
498
  # this is returned by http://services.odata.org/V2/OData/OData.svc
503
499
  415
@@ -517,16 +513,16 @@ module OData
517
513
  end
518
514
  end
519
515
 
520
- def get_coll_odata_h(array:, expand: nil, uribase:, icount: nil)
516
+ def get_coll_odata_h(array:, template:, uribase:, icount: nil)
521
517
  array.map do |w|
522
518
  get_entity_odata_h(entity: w,
523
- expand: expand,
519
+ template: template,
524
520
  uribase: uribase)
525
521
  end
526
522
  end
527
523
 
528
524
  def get_emptycoll_odata_h
529
- [{}]
525
+ EMPTY_HASH_IN_ARY
530
526
  end
531
527
  end
532
528
 
@@ -548,7 +544,7 @@ module OData
548
544
  end
549
545
 
550
546
  def get_emptycoll_odata_h
551
- { 'results' => [{}] }
547
+ { 'results' => EMPTY_HASH_IN_ARY }
552
548
  end
553
549
  end
554
550
 
@@ -570,7 +566,7 @@ module OData
570
566
 
571
567
  def odata_get(req)
572
568
  if req.accept?(APPXML)
573
- [200, CT_APPXML, @service.metadata_xml(req)]
569
+ [200, CT_APPXML, [@service.metadata_xml(req)]]
574
570
  else
575
571
  415
576
572
  end