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 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