safrano 0.3.2 → 0.4.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.
@@ -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