safrano 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6397b8b41d6241576f613abfa0e36c8f6973fa96542b60d388b15954aaa3a92b
4
- data.tar.gz: 3f36fe0d18bd9659d4e3ce27f501ef1540e82f24a26418c28760aae869de85d9
3
+ metadata.gz: 497482614bfc6a87f8a42b9378a4fc8099ddb5b4f180d747b3717a6952f027b0
4
+ data.tar.gz: cd5236302671643fd9fbd9bcd200034f305ce7069e5d56ab2db2225dcfb4ce87
5
5
  SHA512:
6
- metadata.gz: f6f666fb8e2fb136fe7ca0c215e19c24648d9a1a4494c3cdb1a53f45561cf827c2670be56bcf74005828b3a9ebf78e2efc63e8b89db35fefb9f70d4c730e7ebb
7
- data.tar.gz: 2cdfe6b8cb732184dacb256e5c114361f41da91f5a0f821c2f61a5b7ae541b0aa8f5ad45b23598c0d5301311c193f9f6e51d6e8b8def7eac78a54176f92cfd98
6
+ metadata.gz: 4415ca4dc24362f3c179f0cc40f1d73fac6fb7207c0571ef63f1af318fc345e0be8043d8aa1b5ab48e0ead1f5c904dcab07b84061b3ca47f6b7d3c38696d4ae8
7
+ data.tar.gz: 6646d12aadb3827b43c013a3da6c6223d072e5676f8676eac0db59ad8e3ac80d1a64ceb0f5e87d52bb75e5b556eecda701e2d473c4bdd3b650cb680b6ce19b96
@@ -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
@@ -55,6 +55,8 @@ module OData
55
55
  attr_reader :data_fields
56
56
  attr_reader :inlinecount
57
57
  attr_reader :default_template
58
+ attr_reader :uri
59
+ attr_reader :time_cols
58
60
 
59
61
  # Sequel associations pointing to this model. Sequel provides association
60
62
  # reflection information on the "from" side. But in some cases
@@ -90,7 +92,11 @@ module OData
90
92
  # the class-name is not the OData Type. In these subclass we redefine "type_name"
91
93
  # thus when we need the Odata type name, we shall use this method instead of just the collection class name
92
94
  def type_name
93
- to_s
95
+ @type_name
96
+ end
97
+
98
+ def build_type_name
99
+ @type_name = to_s
94
100
  end
95
101
 
96
102
  # convention: default for entity_set_name is the type name
@@ -102,9 +108,15 @@ module OData
102
108
  # TODO: automatically reset all attributes?
103
109
  @deferred_iblock = nil
104
110
  @entity_set_name = nil
111
+ @uri = nil
105
112
  @uparms = nil
106
113
  @params = nil
107
114
  @cx = nil
115
+ @@time_cols = nil
116
+ end
117
+
118
+ def build_uri(uribase)
119
+ @uri = "#{uribase}/#{entity_set_name}"
108
120
  end
109
121
 
110
122
  def execute_deferred_iblock
@@ -159,15 +171,7 @@ module OData
159
171
  end
160
172
 
161
173
  def odata_get_apply_params
162
- begin
163
- @cx = @uparms.apply_to_dataset(@cx)
164
- rescue OData::Filter::Parser::ErrorWrongColumnName
165
- @error = BadRequestFilterParseError
166
- return
167
- rescue OData::Filter::Parser::ErrorFunctionArgumentType
168
- @error = BadRequestFilterParseError
169
- return
170
- end
174
+ @cx = @uparms.apply_to_dataset(@cx)
171
175
  odata_get_inlinecount_w_sequel
172
176
 
173
177
  @cx = @cx.offset(@params['$skip']) if @params['$skip']
@@ -217,17 +221,19 @@ module OData
217
221
  @attribute_path_list = attribute_path_list
218
222
  end
219
223
 
220
- def attribute_path_list(nodes = Set.new)
224
+ MAX_DEPTH = 6
225
+ def attribute_path_list(depth = 0)
226
+ ret = @columns.map(&:to_s)
221
227
  # break circles
222
- return EMPTY_ARRAY if nodes.include?(entity_set_name)
228
+ return ret if depth > MAX_DEPTH
229
+
230
+ depth += 1
223
231
 
224
- ret = @columns.map(&:to_s)
225
- nodes.add entity_set_name
226
232
  @nav_entity_attribs&.each do |a, k|
227
- ret.concat(k.attribute_path_list(nodes).map { |kc| "#{a}/#{kc}" })
233
+ ret.concat(k.attribute_path_list(depth).map { |kc| "#{a}/#{kc}" })
228
234
  end
229
235
  @nav_collection_attribs&.each do |a, k|
230
- ret.concat(k.attribute_path_list(nodes).map { |kc| "#{a}/#{kc}" })
236
+ ret.concat(k.attribute_path_list(depth).map { |kc| "#{a}/#{kc}" })
231
237
  end
232
238
  ret
233
239
  end
@@ -267,15 +273,31 @@ module OData
267
273
  end
268
274
  end
269
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
+
270
296
  # on model class level we return the collection
271
297
  def odata_get(req)
272
298
  @params = req.params
273
- @uribase = req.uribase
274
- initialize_dataset
275
299
 
276
- if (@error = check_url_params)
277
- @error.odata_get(req)
278
- else
300
+ with_validated_get(req) do
279
301
  odata_get_apply_params
280
302
  odata_get_output(req)
281
303
  end
@@ -340,8 +362,7 @@ module OData
340
362
  DJ_OPEN = '{"d":'.freeze
341
363
  DJ_CLOSE = '}'.freeze
342
364
  def to_odata_links_json(service:)
343
- innerj = service.get_coll_odata_links_h(array: get_a,
344
- uribase: @uribase,
365
+ innerj = service.get_coll_odata_links_h(array: @cx.all,
345
366
  icount: @inlinecount).to_json
346
367
  "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
347
368
  end
@@ -417,17 +438,12 @@ module OData
417
438
 
418
439
  def to_odata_json(service:)
419
440
  template = output_template(@uparms)
420
- innerj = service.get_coll_odata_h(array: get_a,
441
+ innerj = service.get_coll_odata_h(array: @cx.all,
421
442
  template: template,
422
- uribase: @uribase,
423
443
  icount: @inlinecount).to_json
424
444
  "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
425
445
  end
426
446
 
427
- def get_a
428
- @cx.all
429
- end
430
-
431
447
  # this functionally similar to the Sequel Rels (many_to_one etc)
432
448
  # We need to base this on the Sequel rels, or extend them
433
449
  def add_nav_prop_collection(assoc_symb, attr_name_str = nil)
@@ -476,14 +492,22 @@ module OData
476
492
  # alias_method :add_nav_prop_single, :addNavEntityAttrib
477
493
 
478
494
  def finalize_publishing
495
+ build_type_name
496
+
479
497
  # finalize media handler
480
498
  @media_handler.register(self) if @media_handler
481
499
 
482
500
  # build default output template structure
483
501
  @default_template = build_default_template
484
502
 
485
- # and finally build the path list
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
486
507
  build_attribute_path_list
508
+
509
+ build_allowed_transitions
510
+ build_entity_allowed_transitions
487
511
  end
488
512
 
489
513
  def prepare_pk
@@ -574,10 +598,38 @@ module OData
574
598
  end
575
599
 
576
600
  def allowed_transitions
577
- [Safrano::TransitionEnd,
578
- Safrano::TransitionCount,
579
- Safrano::Transition.new(entity_id_url_regexp,
580
- 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
581
633
  end
582
634
  end
583
635
  include Transitions
@@ -8,33 +8,13 @@ module OData
8
8
  # this will be mixed in the Model classes (subclasses of Sequel Model)
9
9
  module EntityBase
10
10
  attr_reader :params
11
- attr_reader :uribase
12
11
 
13
12
  include EntityBase::NavigationInfo
14
13
 
15
14
  # methods related to transitions to next state (cf. walker)
16
15
  module Transitions
17
16
  def allowed_transitions
18
- alltr = [
19
- Safrano::TransitionEnd,
20
- Safrano::TransitionCount,
21
- Safrano::TransitionLinks,
22
- Safrano::TransitionValue,
23
- Safrano::Transition.new(self.class.transition_attribute_regexp,
24
- trans: 'transition_attribute')
25
- ]
26
- if (ncurgx = self.class.nav_collection_url_regexp)
27
- alltr <<
28
- Safrano::Transition.new(%r{\A/(#{ncurgx})(.*)\z},
29
- trans: 'transition_nav_collection')
30
-
31
- end
32
- if (neurgx = self.class.nav_entity_url_regexp)
33
- alltr <<
34
- Safrano::Transition.new(%r{\A/(#{neurgx})(.*)\z},
35
- trans: 'transition_nav_entity')
36
- end
37
- alltr
17
+ self.class.entity_allowed_transitions
38
18
  end
39
19
 
40
20
  def transition_end(_match_result)
@@ -90,9 +70,11 @@ module OData
90
70
  @nav_coll
91
71
  end
92
72
 
93
- def uri(uriba)
94
- "#{uriba}/#{self.class.entity_set_name}(#{pk_uri})"
73
+ def uri
74
+ @odata_pk ||= "(#{pk_uri})"
75
+ "#{self.class.uri}#{@odata_pk}"
95
76
  end
77
+
96
78
  D = 'd'.freeze
97
79
  DJ_OPEN = '{"d":'.freeze
98
80
  DJ_CLOSE = '}'.freeze
@@ -101,15 +83,13 @@ module OData
101
83
  def to_odata_json(service:)
102
84
  template = self.class.output_template(@uparms)
103
85
  innerj = service.get_entity_odata_h(entity: self,
104
- template: template,
105
- uribase: @uribase).to_json
86
+ template: template).to_json
106
87
  "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
107
88
  end
108
89
 
109
90
  # Json formatter for a single entity reached by navigation $links
110
91
  def to_odata_onelink_json(service:)
111
- innerj = service.get_entity_odata_link_h(entity: self,
112
- uribase: @uribase).to_json
92
+ innerj = service.get_entity_odata_link_h(entity: self).to_json
113
93
  "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
114
94
  end
115
95
 
@@ -120,36 +100,10 @@ module OData
120
100
  selvals
121
101
  end
122
102
 
123
- # needed for proper datetime output
124
- # TODO: design/performance
125
- def casted_values(cols = nil)
126
- vals = case cols
127
- when nil
128
- values_for_odata
129
- else
130
- selected_values_for_odata(cols)
131
- end
132
-
133
- # WARNING; this code is duplicated in attribute.rb
134
- # (and the inverted transformation is in test/client.rb)
135
- # will require a more systematic solution some day
136
-
137
- vals.transform_values! do |v|
138
- case v
139
- when Time
140
- # try to get back the database time zone and value
141
- (v + v.gmt_offset).utc.to_datetime
142
- else
143
- v
144
- end
145
- end
146
- end
147
-
148
103
  # post paylod expects the new entity in an array
149
104
  def to_odata_post_json(service:)
150
105
  innerj = service.get_coll_odata_h(array: [self],
151
- template: self.class.default_template,
152
- uribase: @uribase).to_json
106
+ template: self.class.default_template).to_json
153
107
  "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
154
108
  end
155
109
 
@@ -159,7 +113,6 @@ module OData
159
113
 
160
114
  def copy_request_infos(req)
161
115
  @params = req.params
162
- @uribase = req.uribase
163
116
  @do_links = req.walker.do_links
164
117
  @uparms = UrlParameters4Single.new(@params)
165
118
  end
@@ -227,7 +180,6 @@ module OData
227
180
  odata_media_value_put(req)
228
181
  elsif req.accept?(APPJSON)
229
182
  data = JSON.parse(req.body.read)
230
- @uribase = req.uribase
231
183
  data.delete('__metadata')
232
184
 
233
185
  if req.in_changeset
@@ -255,8 +207,6 @@ module OData
255
207
  # TODO: check values/types
256
208
 
257
209
  my_data_fields = self.class.data_fields
258
- @uribase = req.uribase
259
- # if req.accept?('application/json')
260
210
 
261
211
  if req.in_changeset
262
212
  set_fields(data, my_data_fields, missing: :skip)
@@ -302,14 +252,30 @@ module OData
302
252
  superclass.type_name
303
253
  end
304
254
 
255
+ def time_cols
256
+ superclass.time_cols
257
+ end
258
+
305
259
  def media_handler
306
260
  superclass.media_handler
307
261
  end
308
262
 
263
+ def uri
264
+ superclass.uri
265
+ end
266
+
309
267
  def default_template
310
268
  superclass.default_template
311
269
  end
312
270
 
271
+ def allowed_transitions
272
+ superclass.allowed_transitions
273
+ end
274
+
275
+ def entity_allowed_transitions
276
+ superclass.entity_allowed_transitions
277
+ end
278
+
313
279
  def to_a
314
280
  y = @child_method.call
315
281
  y.to_a
@@ -364,13 +330,13 @@ module OData
364
330
 
365
331
  module NonMediaEntity
366
332
  # non media entity metadata for json h
367
- def metadata_h(uribase:)
368
- { uri: uri(uribase),
333
+ def metadata_h
334
+ { uri: uri,
369
335
  type: type_name }
370
336
  end
371
337
 
372
338
  def values_for_odata
373
- values.dup
339
+ values
374
340
  end
375
341
 
376
342
  def odata_delete(req)
@@ -398,23 +364,53 @@ module OData
398
364
  end
399
365
  end
400
366
 
367
+ module MappingBeforeOutput
368
+ # needed for proper datetime output
369
+ def casted_values(cols = nil)
370
+ vals = case cols
371
+ when nil
372
+ # we need to dup the model values as we need to change it before passing to_json,
373
+ # but we dont want to interfere with Sequel's owned data
374
+ # (eg because then in worst case it could happen that we write back changed values to DB)
375
+ values_for_odata.dup
376
+ else
377
+ selected_values_for_odata(cols)
378
+ end
379
+ self.class.time_cols.each { |tc| vals[tc] = vals[tc]&.iso8601 if vals.key?(tc) }
380
+ vals
381
+ end
382
+ end
383
+ module NoMappingBeforeOutput
384
+ # current model does not have eg. Time fields--> no special mapping, just to_json is fine
385
+ # --> we can use directly the model.values (values_for_odata) withoud dup'ing it as we dont
386
+ # need to change it, just output as is
387
+ def casted_values(cols = nil)
388
+ case cols
389
+ when nil
390
+ values_for_odata
391
+ else
392
+ selected_values_for_odata(cols)
393
+ end
394
+ end
395
+ end
396
+
401
397
  module MediaEntity
402
398
  # media entity metadata for json h
403
- def metadata_h(uribase:)
404
- { uri: uri(uribase),
399
+ def metadata_h
400
+ { uri: uri,
405
401
  type: type_name,
406
- media_src: media_src(uribase),
407
- edit_media: edit_media(uribase),
402
+ media_src: media_src,
403
+ edit_media: edit_media,
408
404
  content_type: @values[:content_type] }
409
405
  end
410
406
 
411
- def media_src(uribase)
407
+ def media_src
412
408
  version = self.class.media_handler.ressource_version(self)
413
- "#{uri(uribase)}/$value?version=#{version}"
409
+ "#{uri}/$value?version=#{version}"
414
410
  end
415
411
 
416
- def edit_media(uribase)
417
- "#{uri(uribase)}/$value"
412
+ def edit_media
413
+ "#{uri}/$value"
418
414
  end
419
415
 
420
416
  # directory where to put/find the media files for this entity-type