safrano 0.3.3 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/odata/attribute.rb +9 -8
  3. data/lib/odata/batch.rb +8 -8
  4. data/lib/odata/collection.rb +239 -92
  5. data/lib/odata/collection_filter.rb +40 -9
  6. data/lib/odata/collection_media.rb +159 -28
  7. data/lib/odata/collection_order.rb +46 -36
  8. data/lib/odata/common_logger.rb +37 -12
  9. data/lib/odata/entity.rb +188 -99
  10. data/lib/odata/error.rb +60 -12
  11. data/lib/odata/expand.rb +123 -0
  12. data/lib/odata/filter/base.rb +66 -0
  13. data/lib/odata/filter/error.rb +33 -0
  14. data/lib/odata/filter/parse.rb +6 -12
  15. data/lib/odata/filter/sequel.rb +42 -29
  16. data/lib/odata/filter/sequel_function_adapter.rb +147 -0
  17. data/lib/odata/filter/token.rb +5 -1
  18. data/lib/odata/filter/tree.rb +45 -29
  19. data/lib/odata/navigation_attribute.rb +60 -27
  20. data/lib/odata/relations.rb +2 -2
  21. data/lib/odata/select.rb +42 -0
  22. data/lib/odata/url_parameters.rb +51 -36
  23. data/lib/odata/walker.rb +6 -6
  24. data/lib/safrano.rb +23 -13
  25. data/lib/{safrano_core.rb → safrano/core.rb} +12 -4
  26. data/lib/{multipart.rb → safrano/multipart.rb} +17 -26
  27. data/lib/{odata_rack_builder.rb → safrano/odata_rack_builder.rb} +0 -1
  28. data/lib/{rack_app.rb → safrano/rack_app.rb} +12 -10
  29. data/lib/{request.rb → safrano/request.rb} +8 -14
  30. data/lib/{response.rb → safrano/response.rb} +1 -2
  31. data/lib/{sequel_join_by_paths.rb → safrano/sequel_join_by_paths.rb} +1 -1
  32. data/lib/{service.rb → safrano/service.rb} +162 -131
  33. data/lib/safrano/version.rb +3 -0
  34. data/lib/sequel/plugins/join_by_paths.rb +11 -10
  35. metadata +33 -16
  36. data/lib/version.rb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b25412d7e053724965ba296bdd43a6f0b3bbd8a62cee2f45a14edf18303ed6c1
4
- data.tar.gz: 1e9ad5ce553d13199e4ce79cecf7209ce1bb2695f5c476d8f5e5e16bd57bd4f9
3
+ metadata.gz: 497482614bfc6a87f8a42b9378a4fc8099ddb5b4f180d747b3717a6952f027b0
4
+ data.tar.gz: cd5236302671643fd9fbd9bcd200034f305ce7069e5d56ab2db2225dcfb4ce87
5
5
  SHA512:
6
- metadata.gz: ad1361bb963f77870f784ed5322318b2567bac06e6ef915dcda62f57df767a0893a988672e4ace8dbd64cdda0cbff8d78de2aa712d4ebceb8821ed109018c909
7
- data.tar.gz: d4f5f16fa548883e697479f313aa824c0798c38d71ead64172552ddfaec79fb53652063bf6dbfe36362cca5c30a80534665e8a12895fb2f343f1857a0dba8c1c
6
+ metadata.gz: 4415ca4dc24362f3c179f0cc40f1d73fac6fb7207c0571ef63f1af318fc345e0be8043d8aa1b5ab48e0ead1f5c904dcab07b84061b3ca47f6b7d3c38696d4ae8
7
+ data.tar.gz: 6646d12aadb3827b43c013a3da6c6223d072e5676f8676eac0db59ad8e3ac80d1a64ceb0f5e87d52bb75e5b556eecda701e2d473c4bdd3b650cb680b6ce19b96
@@ -1,5 +1,5 @@
1
1
  require 'json'
2
- require_relative '../safrano_core.rb'
2
+ require_relative '../safrano/core.rb'
3
3
  require_relative './entity.rb'
4
4
 
5
5
  module OData
@@ -13,15 +13,14 @@ module OData
13
13
  end
14
14
 
15
15
  def value
16
- # WARNING; this code is duplicated in entity.rb
17
- # (and the inverted transformation is in test/client.rb)
18
- # will require a more systematic solution some day
19
- # WARNING 2... this require more work to handle the timezones topci
16
+ # WARNING ... this require more work to handle the timezones topci
20
17
  # currently it is just set to make some minimal testcase work
21
18
  case (v = @entity.values[@name.to_sym])
22
19
  when Time
23
20
  # try to get back the database time zone and value
24
- (v + v.gmt_offset).utc.to_datetime
21
+ # (v + v.gmt_offset).utc.to_datetime
22
+ v.iso8601
23
+
25
24
  else
26
25
  v
27
26
  end
@@ -57,9 +56,11 @@ module OData
57
56
  [self, :end_with_value]
58
57
  end
59
58
 
59
+ ALLOWED_TRANSITIONS = [Safrano::TransitionEnd,
60
+ Safrano::TransitionValue].freeze
61
+
60
62
  def allowed_transitions
61
- [Safrano::TransitionEnd,
62
- Safrano::TransitionValue]
63
+ ALLOWED_TRANSITIONS
63
64
  end
64
65
  end
65
66
  include Transitions
@@ -1,5 +1,5 @@
1
- require 'rack_app.rb'
2
- require 'safrano_core.rb'
1
+ require_relative '../safrano/rack_app.rb'
2
+ require_relative '../safrano/core.rb'
3
3
  require 'rack/body_proxy'
4
4
  require_relative './common_logger.rb'
5
5
 
@@ -59,7 +59,7 @@ module OData
59
59
  # TODO: test ?
60
60
  if (logga = @full_req.env['safrano.logger_mw'])
61
61
  logga.batch_log(env, status, header, began_at)
62
- # TODO check why/if we need Rack::Utils::HeaderHash.new(header)
62
+ # TODO: check why/if we need Rack::Utils::HeaderHash.new(header)
63
63
  # and Rack::BodyProxy.new(body) ?
64
64
  end
65
65
  [status, header, body]
@@ -71,7 +71,7 @@ module OData
71
71
 
72
72
  headers.each do |name, value|
73
73
  env_key = name.upcase.tr('-', '_')
74
- env_key = 'HTTP_' + env_key unless env_key == 'CONTENT_TYPE'
74
+ env_key = "HTTP_#{env_key}" unless env_key == 'CONTENT_TYPE'
75
75
  converted_headers[env_key] = value
76
76
  end
77
77
 
@@ -104,11 +104,11 @@ module OData
104
104
  # jaune d'oeuf
105
105
  class DisabledHandler < HandlerBase
106
106
  def odata_post(_req)
107
- [404, {}, '$batch is not enabled ']
107
+ [404, EMPTY_HASH, '$batch is not enabled ']
108
108
  end
109
109
 
110
110
  def odata_get(_req)
111
- [404, {}, '$batch is not enabled ']
111
+ [404, EMPTY_HASH, '$batch is not enabled ']
112
112
  end
113
113
  end
114
114
  # battre le tout
@@ -138,12 +138,12 @@ module OData
138
138
 
139
139
  [202, resp_hdrs, @mult_response.body[0]]
140
140
  else
141
- [415, {}, 'Unsupported Media Type']
141
+ [415, EMPTY_HASH, 'Unsupported Media Type']
142
142
  end
143
143
  end
144
144
 
145
145
  def odata_get(_req)
146
- [405, {}, 'You cant GET $batch, POST it ']
146
+ [405, EMPTY_HASH, 'You cant GET $batch, POST it ']
147
147
  end
148
148
  end
149
149
  end
@@ -4,12 +4,14 @@
4
4
 
5
5
  require 'json'
6
6
  require 'rexml/document'
7
- require 'safrano_core.rb'
8
- require 'odata/error.rb'
9
- require 'odata/collection_filter.rb'
10
- require 'odata/collection_order.rb'
11
- require 'odata/url_parameters.rb'
12
- require 'odata/collection_media.rb'
7
+ require_relative '../safrano/core.rb'
8
+ require_relative 'error.rb'
9
+ require_relative 'collection_filter.rb'
10
+ require_relative 'collection_order.rb'
11
+ require_relative 'expand.rb'
12
+ require_relative 'select.rb'
13
+ require_relative 'url_parameters.rb'
14
+ require_relative 'collection_media.rb'
13
15
 
14
16
  # small helper method
15
17
  # http://stackoverflow.com/
@@ -43,16 +45,25 @@ module OData
43
45
  # we will add this to our Model classes with "extend" --> self is the Class
44
46
  module EntityClassBase
45
47
  SINGLE_PK_URL_REGEXP = /\A\(\s*'?([\w\s]+)'?\s*\)(.*)/.freeze
46
- ONLY_INTEGER_RGX = /\A[+-]?\d+\z/
48
+ ONLY_INTEGER_RGX = /\A[+-]?\d+\z/.freeze
47
49
 
48
50
  attr_reader :nav_collection_url_regexp
49
51
  attr_reader :nav_entity_url_regexp
50
52
  attr_reader :entity_id_url_regexp
51
- attr_reader :attrib_paths_url_regexp
52
53
  attr_reader :nav_collection_attribs
53
54
  attr_reader :nav_entity_attribs
54
55
  attr_reader :data_fields
55
56
  attr_reader :inlinecount
57
+ attr_reader :default_template
58
+ attr_reader :uri
59
+ attr_reader :time_cols
60
+
61
+ # Sequel associations pointing to this model. Sequel provides association
62
+ # reflection information on the "from" side. But in some cases
63
+ # we will need the reverted way
64
+ # finally not needed and not used yet
65
+ # attr_accessor :assocs_to
66
+
56
67
  # set to parent entity in case the collection is a nav.collection
57
68
  # nil otherwise
58
69
  attr_reader :nav_parent
@@ -62,9 +73,6 @@ module OData
62
73
  # dataset
63
74
  attr_accessor :cx
64
75
 
65
- # array of the objects --> dataset.to_a
66
- attr_accessor :ax
67
-
68
76
  # url params
69
77
  attr_reader :params
70
78
 
@@ -84,7 +92,11 @@ module OData
84
92
  # the class-name is not the OData Type. In these subclass we redefine "type_name"
85
93
  # thus when we need the Odata type name, we shall use this method instead of just the collection class name
86
94
  def type_name
87
- to_s
95
+ @type_name
96
+ end
97
+
98
+ def build_type_name
99
+ @type_name = to_s
88
100
  end
89
101
 
90
102
  # convention: default for entity_set_name is the type name
@@ -96,10 +108,15 @@ module OData
96
108
  # TODO: automatically reset all attributes?
97
109
  @deferred_iblock = nil
98
110
  @entity_set_name = nil
111
+ @uri = nil
99
112
  @uparms = nil
100
113
  @params = nil
101
- @ax = nil
102
114
  @cx = nil
115
+ @@time_cols = nil
116
+ end
117
+
118
+ def build_uri(uribase)
119
+ @uri = "#{uribase}/#{entity_set_name}"
103
120
  end
104
121
 
105
122
  def execute_deferred_iblock
@@ -123,8 +140,7 @@ module OData
123
140
  new_entity.save(transaction: false)
124
141
  OData.create_nav_relation(new_entity, assoc, parent)
125
142
  parent.save(transaction: false)
126
- else
127
- # not supported
143
+ # else # not supported
128
144
  end
129
145
  end
130
146
  def odata_create_save_entity_and_rel(req, new_entity, assoc, parent)
@@ -155,15 +171,7 @@ module OData
155
171
  end
156
172
 
157
173
  def odata_get_apply_params
158
- begin
159
- @cx = @uparms.apply_to_dataset(@cx)
160
- rescue OData::Filter::Parser::ErrorWrongColumnName
161
- @error = BadRequestFilterParseError
162
- return
163
- rescue OData::Filter::Parser::ErrorFunctionArgumentType
164
- @error = BadRequestFilterParseError
165
- return
166
- end
174
+ @cx = @uparms.apply_to_dataset(@cx)
167
175
  odata_get_inlinecount_w_sequel
168
176
 
169
177
  @cx = @cx.offset(@params['$skip']) if @params['$skip']
@@ -192,9 +200,7 @@ module OData
192
200
  def check_u_p_inlinecount
193
201
  return unless (icp = @params['$inlinecount'])
194
202
 
195
- unless (icp == 'allpages') || (icp == 'none')
196
- return BadRequestInlineCountParamError
197
- end
203
+ return BadRequestInlineCountParamError unless (icp == 'allpages') || (icp == 'none')
198
204
 
199
205
  nil
200
206
  end
@@ -207,26 +213,27 @@ module OData
207
213
  @uparms.check_order
208
214
  end
209
215
 
216
+ def check_u_p_expand
217
+ @uparms.check_expand
218
+ end
219
+
210
220
  def build_attribute_path_list
211
221
  @attribute_path_list = attribute_path_list
212
- @attrib_paths_url_regexp = @attribute_path_list.join('|')
213
222
  end
214
223
 
215
- def attribute_path_list(nodes = Set.new)
224
+ MAX_DEPTH = 6
225
+ def attribute_path_list(depth = 0)
226
+ ret = @columns.map(&:to_s)
216
227
  # break circles
217
- return [] if nodes.include?(entity_set_name)
228
+ return ret if depth > MAX_DEPTH
218
229
 
219
- ret = @columns.map(&:to_s)
220
- nodes.add entity_set_name
221
- if @nav_entity_attribs
222
- @nav_entity_attribs.each do |a, k|
223
- ret.concat(k.attribute_path_list(nodes).map { |kc| "#{a}/#{kc}" })
224
- end
230
+ depth += 1
231
+
232
+ @nav_entity_attribs&.each do |a, k|
233
+ ret.concat(k.attribute_path_list(depth).map { |kc| "#{a}/#{kc}" })
225
234
  end
226
- if @nav_collection_attribs
227
- @nav_collection_attribs.each do |a, k|
228
- ret.concat(k.attribute_path_list(nodes).map { |kc| "#{a}/#{kc}" })
229
- end
235
+ @nav_collection_attribs&.each do |a, k|
236
+ ret.concat(k.attribute_path_list(depth).map { |kc| "#{a}/#{kc}" })
230
237
  end
231
238
  ret
232
239
  end
@@ -235,20 +242,18 @@ module OData
235
242
  return nil unless @params
236
243
 
237
244
  check_u_p_top || check_u_p_skip || check_u_p_orderby ||
238
- check_u_p_filter || check_u_p_inlinecount
245
+ check_u_p_filter || check_u_p_expand || check_u_p_inlinecount
239
246
  end
240
247
 
241
248
  def initialize_dataset
242
249
  @cx = self
243
- @ax = nil
244
250
  @cx = navigated_dataset if @cx.nav_parent
245
251
  @model = if @cx.respond_to? :model
246
252
  @cx.model
247
253
  else
248
254
  @cx
249
255
  end
250
- @jh = @model.join_by_paths_helper
251
- @uparms = UrlParameters.new(@jh, @params)
256
+ @uparms = UrlParameters4Coll.new(@model, @params)
252
257
  end
253
258
 
254
259
  # finally return the requested output according to format, options etc
@@ -268,15 +273,31 @@ module OData
268
273
  end
269
274
  end
270
275
 
276
+ # validation/error handling methods.
277
+ # normal processing is done in the passed proc
278
+
279
+ def with_validated_get(req)
280
+ begin
281
+ initialize_dataset
282
+ return yield unless (@error = check_url_params)
283
+ rescue OData::Filter::Parser::ErrorWrongColumnName
284
+ @error = BadRequestFilterParseError
285
+ rescue OData::Filter::Parser::ErrorFunctionArgumentType
286
+ @error = BadRequestFilterParseError
287
+ rescue OData::Filter::FunctionNotImplemented => e
288
+ @error = e.odata_error
289
+ rescue OData::Filter::Parser::ErrorInvalidFunction => e
290
+ @error = e.odata_error
291
+ end
292
+
293
+ @error.odata_get(req) if @error
294
+ end
295
+
271
296
  # on model class level we return the collection
272
297
  def odata_get(req)
273
298
  @params = req.params
274
- @uribase = req.uribase
275
- initialize_dataset
276
299
 
277
- if (@error = check_url_params)
278
- @error.odata_get(req)
279
- else
300
+ with_validated_get(req) do
280
301
  odata_get_apply_params
281
302
  odata_get_output(req)
282
303
  end
@@ -288,7 +309,11 @@ module OData
288
309
 
289
310
  # add metadata xml to the passed REXML schema object
290
311
  def add_metadata_rexml(schema)
291
- enty = schema.add_element('EntityType', 'Name' => to_s)
312
+ enty = if @media_handler
313
+ schema.add_element('EntityType', 'Name' => to_s, 'HasStream' => 'true')
314
+ else
315
+ schema.add_element('EntityType', 'Name' => to_s)
316
+ end
292
317
  # with their properties
293
318
  db_schema.each do |pnam, prop|
294
319
  if prop[:primary_key] == true
@@ -304,47 +329,119 @@ module OData
304
329
  end
305
330
 
306
331
  # metadata REXML data for a single Nav attribute
307
- def metadata_nav_rexml_attribs(assoc, cmap, relman, xnamespace)
332
+ def metadata_nav_rexml_attribs(assoc, to_klass, relman, xnamespace)
308
333
  from = type_name
309
- to = cmap[assoc.to_s].type_name
334
+ to = to_klass.type_name
310
335
  relman.get_metadata_xml_attribs(from,
311
336
  to,
312
- association_reflection(assoc)[:type],
313
- xnamespace)
337
+ association_reflection(assoc.to_sym)[:type],
338
+ xnamespace,
339
+ assoc)
314
340
  end
315
341
 
316
342
  # and their Nav attributes == Sequel Model association
317
- def add_metadata_navs_rexml(schema_enty, cmap, relman, xnamespace)
318
- associations.each do |assoc|
319
- # associated objects need to be in the map...
320
- next unless cmap[assoc.to_s]
321
-
322
- nattrs = metadata_nav_rexml_attribs(assoc, cmap, relman, xnamespace)
343
+ def add_metadata_navs_rexml(schema_enty, relman, xnamespace)
344
+ @nav_entity_attribs&.each do |ne, klass|
345
+ nattr = metadata_nav_rexml_attribs(ne,
346
+ klass,
347
+ relman,
348
+ xnamespace)
349
+ schema_enty.add_element('NavigationProperty', nattr)
350
+ end
323
351
 
324
- schema_enty.add_element('NavigationProperty', nattrs)
352
+ @nav_collection_attribs&.each do |nc, klass|
353
+ nattr = metadata_nav_rexml_attribs(nc,
354
+ klass,
355
+ relman,
356
+ xnamespace)
357
+ schema_enty.add_element('NavigationProperty', nattr)
325
358
  end
326
359
  end
327
360
 
328
361
  D = 'd'.freeze
329
- DJopen = '{"d":'.freeze
330
- DJclose = '}'.freeze
362
+ DJ_OPEN = '{"d":'.freeze
363
+ DJ_CLOSE = '}'.freeze
331
364
  def to_odata_links_json(service:)
332
- innerj = service.get_coll_odata_links_h(array: get_a,
333
- uribase: @uribase,
365
+ innerj = service.get_coll_odata_links_h(array: @cx.all,
334
366
  icount: @inlinecount).to_json
335
- "#{DJopen}#{innerj}#{DJclose}"
367
+ "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
336
368
  end
337
369
 
338
- def to_odata_json(service:)
339
- innerj = service.get_coll_odata_h(array: get_a,
340
- expand: @params['$expand'],
341
- uribase: @uribase,
342
- icount: @inlinecount).to_json
343
- "#{DJopen}#{innerj}#{DJclose}"
370
+ # def output_template(expand: nil, select: nil)
371
+ def output_template(uparms)
372
+ # output_template_deep(expand_list: expand_list, select: select)
373
+ output_template_deep(expand_list: uparms.expand.template, select: uparms.select)
374
+ end
375
+
376
+ # Recursive
377
+ def output_template_deep(expand_list:, select: OData::SelectBase::ALL)
378
+ return default_template if expand_list.empty? && select.all_props?
379
+
380
+ template = {}
381
+ expand_e = {}
382
+ expand_c = {}
383
+ deferr = []
384
+
385
+ # 1. handle non-navigation properties, only consider $select
386
+ # 2. handle navigations properties, need to check $select and $expand
387
+ if select.all_props?
388
+ template[:all_values] = EMPTYH
389
+ # include all nav attributes -->
390
+ @nav_entity_attribs&.each do |attr, klass|
391
+ if expand_list.key?(attr)
392
+ expand_e[attr] = klass.output_template_deep(expand_list: expand_list[attr])
393
+ else
394
+ deferr << attr
395
+ end
396
+ end
397
+
398
+ @nav_collection_attribs&.each do |attr, klass|
399
+ if expand_list.key?(attr)
400
+ expand_c[attr] = klass.output_template_deep(expand_list: expand_list[attr])
401
+ else
402
+ deferr << attr
403
+ end
404
+ end
405
+
406
+ else
407
+ template[:selected_vals] = @columns.map(&:to_s) & select.props
408
+ # include only selected nav attribs-->need additional intersection step
409
+ if @nav_entity_attribs
410
+ selected_nav_e = @nav_entity_attribs.keys & select.props
411
+
412
+ selected_nav_e&.each do |attr|
413
+ if expand_list.key?(attr)
414
+ klass = @nav_entity_attribs[attr]
415
+ expand_e[attr] = klass.output_template_deep(expand_list: expand_list[attr])
416
+ else
417
+ deferr << attr
418
+ end
419
+ end
420
+ end
421
+ if @nav_collection_attribs
422
+ selected_nav_c = @nav_collection_attribs.keys & select.props
423
+ selected_nav_c&.each do |attr|
424
+ if expand_list.key?(attr)
425
+ klass = @nav_collection_attribs[attr]
426
+ expand_c[attr] = klass.output_template_deep(expand_list: expand_list[attr])
427
+ else
428
+ deferr << attr
429
+ end
430
+ end
431
+ end
432
+ end
433
+ template[:expand_e] = expand_e if expand_e
434
+ template[:expand_c] = expand_c if expand_c
435
+ template[:deferr] = deferr if deferr
436
+ template
344
437
  end
345
438
 
346
- def get_a
347
- @ax.nil? ? @cx.to_a : @ax
439
+ def to_odata_json(service:)
440
+ template = output_template(@uparms)
441
+ innerj = service.get_coll_odata_h(array: @cx.all,
442
+ template: template,
443
+ icount: @inlinecount).to_json
444
+ "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
348
445
  end
349
446
 
350
447
  # this functionally similar to the Sequel Rels (many_to_one etc)
@@ -356,9 +453,8 @@ module OData
356
453
  assoc = all_association_reflections.find do |a|
357
454
  a[:name] == assoc_symb && a[:model] == self
358
455
  end
359
- unless assoc
360
- raise OData::API::ModelAssociationNameError.new(self, assoc_symb)
361
- end
456
+
457
+ raise OData::API::ModelAssociationNameError.new(self, assoc_symb) unless assoc
362
458
 
363
459
  attr_class = assoc[:class_name].constantize
364
460
  lattr_name_str = (attr_name_str || assoc_symb.to_s)
@@ -373,9 +469,8 @@ module OData
373
469
  assoc = all_association_reflections.find do |a|
374
470
  a[:name] == assoc_symb && a[:model] == self
375
471
  end
376
- unless assoc
377
- raise OData::API::ModelAssociationNameError.new(self, assoc_symb)
378
- end
472
+
473
+ raise OData::API::ModelAssociationNameError.new(self, assoc_symb) unless assoc
379
474
 
380
475
  attr_class = assoc[:class_name].constantize
381
476
  lattr_name_str = (attr_name_str || assoc_symb.to_s)
@@ -383,10 +478,38 @@ module OData
383
478
  @nav_entity_url_regexp = @nav_entity_attribs.keys.join('|')
384
479
  end
385
480
 
481
+ EMPTYH = {}.freeze
482
+
483
+ def build_default_template
484
+ template = { all_values: EMPTYH }
485
+ if @nav_entity_attribs || @nav_collection_attribs
486
+ template[:deferr] = (@nav_entity_attribs&.keys || []) + (@nav_collection_attribs&.keys || EMPTY_ARRAY)
487
+ end
488
+ template
489
+ end
386
490
  # old names...
387
491
  # alias_method :add_nav_prop_collection, :addNavCollectionAttrib
388
492
  # alias_method :add_nav_prop_single, :addNavEntityAttrib
389
493
 
494
+ def finalize_publishing
495
+ build_type_name
496
+
497
+ # finalize media handler
498
+ @media_handler.register(self) if @media_handler
499
+
500
+ # build default output template structure
501
+ @default_template = build_default_template
502
+
503
+ # Time columns
504
+ @time_cols = db_schema.select { |c, v| v[:type] == :datetime }.map { |c, v| c }
505
+
506
+ # and finally build the path list and allowed tr's
507
+ build_attribute_path_list
508
+
509
+ build_allowed_transitions
510
+ build_entity_allowed_transitions
511
+ end
512
+
390
513
  def prepare_pk
391
514
  if primary_key.is_a? Array
392
515
  @pk_names = []
@@ -435,11 +558,7 @@ module OData
435
558
  def check_odata_val_type(val, type)
436
559
  case type
437
560
  when :integer
438
- if (val =~ ONLY_INTEGER_RGX)
439
- [true, Integer(val)]
440
- else
441
- [false, val]
442
- end
561
+ val =~ ONLY_INTEGER_RGX ? [true, Integer(val)] : [false, val]
443
562
  when :string
444
563
  [true, val]
445
564
  else
@@ -479,10 +598,38 @@ module OData
479
598
  end
480
599
 
481
600
  def allowed_transitions
482
- [Safrano::TransitionEnd,
483
- Safrano::TransitionCount,
484
- Safrano::Transition.new(entity_id_url_regexp,
485
- trans: 'transition_id')]
601
+ @allowed_transitions
602
+ end
603
+
604
+ def entity_allowed_transitions
605
+ @entity_allowed_transitions
606
+ end
607
+
608
+ def build_allowed_transitions
609
+ @allowed_transitions = [Safrano::TransitionEnd,
610
+ Safrano::TransitionCount,
611
+ Safrano::Transition.new(entity_id_url_regexp,
612
+ trans: 'transition_id')].freeze
613
+ end
614
+
615
+ def build_entity_allowed_transitions
616
+ @entity_allowed_transitions = [
617
+ Safrano::TransitionEnd,
618
+ Safrano::TransitionCount,
619
+ Safrano::TransitionLinks,
620
+ Safrano::TransitionValue,
621
+ Safrano::Transition.new(transition_attribute_regexp, trans: 'transition_attribute')
622
+ ]
623
+ if (ncurgx = @nav_collection_url_regexp)
624
+ @entity_allowed_transitions <<
625
+ Safrano::Transition.new(%r{\A/(#{ncurgx})(.*)\z}, trans: 'transition_nav_collection')
626
+ end
627
+ if (neurgx = @nav_entity_url_regexp)
628
+ @entity_allowed_transitions <<
629
+ Safrano::Transition.new(%r{\A/(#{neurgx})(.*)\z}, trans: 'transition_nav_entity')
630
+ end
631
+ @entity_allowed_transitions.freeze
632
+ @entity_allowed_transitions
486
633
  end
487
634
  end
488
635
  include Transitions
@@ -500,7 +647,7 @@ module OData
500
647
  md.shift # remove first element which is the whole match
501
648
  mdc = []
502
649
  error = false
503
- primary_key.each_with_index { |pk, i|
650
+ primary_key.each_with_index do |pk, i|
504
651
  ck, casted = check_odata_val_type(md[i], db_schema[pk][:type])
505
652
  if ck
506
653
  mdc << casted
@@ -508,7 +655,7 @@ module OData
508
655
  error = true
509
656
  break
510
657
  end
511
- }
658
+ end
512
659
  if error
513
660
  [false, md]
514
661
  else
@@ -538,7 +685,7 @@ module OData
538
685
  # validate payload column names
539
686
  if (invalid = invalid_hash_data?(data))
540
687
  ::OData::Request::ON_CGST_ERROR.call(req)
541
- return [422, {}, ['Invalid attribute name: ', invalid.to_s]]
688
+ return [422, EMPTY_HASH, ['Invalid attribute name: ', invalid.to_s]]
542
689
  end
543
690
 
544
691
  if req.accept?(APPJSON)