safrano 0.5.1 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b38827d37fa3bfed54a30aa61c04b5da27470071c5e3167fb5a08aa5c48a69af
4
- data.tar.gz: 3754e63822b6c504b42bc698df360295ab92bcf96a2ed8fdef4af7f2ed5c8d85
3
+ metadata.gz: 00bf6cd3a561928000b8bf53bbc71f139b162f92adc229e625f010dc6f1ce3c7
4
+ data.tar.gz: bf154e1b55e4d32e4640d6c993566a45eae8a2bce44b23dd979a9946d3633dc7
5
5
  SHA512:
6
- metadata.gz: 0c6a3949c741f120955b4582ddad95aa3fefe80f613030970c174b4fe000129e4d2f67dcb43deae5608a2a287a489a4c94d6ba0df5a613c5557b22b8ca4f625b
7
- data.tar.gz: 7e5070fa657435fbc3a2932344392e58fa7105863bb045ad5a0aac7227a1e7ce5e4f54628915c81156da2f315eb359409c786243c47697877237ac08294a7872
6
+ metadata.gz: 9b2fad0321b45bae50f3435b688b31f99d320cfa4f81f14867f64659faa0c4e8ece7bc4f1f7ecc2db2c7710c08f7726bb4d758bf5d09d487a54199afea50119a
7
+ data.tar.gz: b9a70b4a08fb7a2cc31df7e7637f635a42a85433660f5061f6897e1fb463496252c8d611fb1023e5f27202bbc478b58569d2ad42b1c3359db14e34c92f4ec35c
@@ -60,10 +60,13 @@ module Safrano
60
60
  end
61
61
 
62
62
  def initialize_dataset(dtset = nil)
63
- @cx = dtset || @modelk
63
+ @cx = @cx || dtset || @modelk
64
+ end
65
+
66
+ def initialize_uparms
64
67
  @uparms = UrlParameters4Coll.new(@cx, @params)
65
68
  end
66
-
69
+
67
70
  def odata_get_apply_params
68
71
  @uparms.apply_to_dataset(@cx).map_result! do |dataset|
69
72
  @cx = dataset
@@ -131,9 +134,9 @@ module Safrano
131
134
 
132
135
  # on model class level we return the collection
133
136
  def odata_get(req)
134
- @params = req.params
135
- initialize_dataset
136
-
137
+ @params = @params || req.params
138
+ initialize_dataset
139
+ initialize_uparms
137
140
  @uparms.check_all.if_valid { |_ret|
138
141
  odata_get_apply_params.if_valid { |_ret|
139
142
  odata_get_output(req)
@@ -170,8 +173,7 @@ module Safrano
170
173
  end
171
174
 
172
175
  def initialize_dataset(dtset = nil)
173
- @cx = dtset || navigated_dataset
174
- @uparms = UrlParameters4Coll.new(@cx, @params)
176
+ @cx = @cx || dtset || navigated_dataset
175
177
  end
176
178
  # redefinitions of the main methods for a navigated collection
177
179
  # (eg. all Books of Author[2] is Author[2].Books.all )
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Safrano
4
4
  module FunctionImport
5
+ EMPTY_HASH = {}.freeze
5
6
  class ResultDefinition
6
7
  D = 'd'
7
8
  DJ_OPEN = '{"d":'
@@ -11,33 +12,104 @@ module Safrano
11
12
  VALUEK = 'value'
12
13
  RESULTSK = 'results'
13
14
  COLLECTION = 'Collection'
14
-
15
- def initialize(klassmod)
16
- @klassmod = klassmod
15
+
16
+ def allowed_transitions
17
+ [Safrano::TransitionEnd]
17
18
  end
18
-
19
- def to_odata_json(result, _req)
20
- "#{DJ_OPEN}#{result.odata_h.to_json}#{DJ_CLOSE}"
19
+
20
+ def transition_end(_match_result)
21
+ Safrano::Transition::RESULT_END
21
22
  end
22
-
23
- def type_metadata
23
+
24
+ # we will have this on class and instance level for making things simpler first
25
+ def self.klassmod
26
+ @klassmod
27
+ end
28
+
29
+ # return a subclass of ResultAsComplexType
30
+ def self.asComplexType(klassmod)
31
+ Class.new(ResultAsComplexType) do
32
+ @klassmod = klassmod
33
+ end
34
+ end
35
+
36
+ # return a subclass of ResultAsComplexType
37
+ def self.asComplexTypeColl(klassmod)
38
+ Class.new(ResultAsComplexTypeColl) do
39
+ @klassmod = klassmod
40
+ end
41
+ end
42
+
43
+ def self.asPrimitiveType(klassmod)
44
+ Class.new(ResultAsPrimitiveType) do
45
+ @klassmod = klassmod
46
+ end
47
+ end
48
+
49
+ def self.asPrimitiveTypeColl(klassmod)
50
+ Class.new(ResultAsPrimitiveTypeColl) do
51
+ @klassmod = klassmod
52
+ end
53
+ end
54
+
55
+ def self.asEntity(klassmod)
56
+ Class.new(ResultAsEntity) do
57
+ @klassmod = klassmod
58
+ end
59
+ end
60
+
61
+ def self.asEntityColl(klassmod)
62
+ Class.new(ResultAsEntityColl) do
63
+ @klassmod = klassmod
64
+ end
65
+ end
66
+
67
+ def initialize(value)
68
+ @value = value
69
+ end
70
+
71
+ def odata_get(req)
72
+ [200, EMPTY_HASH, [to_odata_json(req)]]
73
+ end
74
+ def self.type_metadata
24
75
  @klassmod.type_name
25
76
  end
77
+ def type_metadata
78
+ self.class.type_metadata
79
+ end
80
+
81
+ # needed for ComplexType result
82
+ def to_odata_json(_req)
83
+ "#{DJ_OPEN}#{@value.odata_h.to_json}#{DJ_CLOSE}"
84
+ end
85
+
86
+ # wrapper
87
+ # for OData Entity and Collections, return them directly
88
+ # for others, ie ComplexType, Prims etc, return the ResultDefinition-subclass wrapped result
89
+ def self.do_execute_func_result(result, _req, apply_query_params=false)
90
+ self.new(result)
91
+ end
92
+
26
93
  end
94
+
27
95
  class ResultAsComplexType < ResultDefinition
96
+ def self.type_metadata
97
+ @klassmod.type_name
98
+ end
28
99
  end
100
+
29
101
  class ResultAsComplexTypeColl < ResultDefinition
30
- def type_metadata
102
+ def self.type_metadata
31
103
  "Collection(#{@klassmod.type_name})"
32
104
  end
33
105
 
34
- def to_odata_json(coll, req)
106
+ def to_odata_json(req)
35
107
  # "#{DJ_OPEN}#{{ RESULTSK => coll.map { |c| c.odata_h } }.to_json}#{DJ_CLOSE}"
36
- template = @klassmod.output_template
108
+ template = self.class.klassmod.output_template
37
109
  # TODO: Error handling if database contains binary BLOB data that cant be
38
110
  # interpreted as UTF-8 then JSON will fail here
39
111
 
40
- innerh = req.service.get_coll_odata_h(array: coll,
112
+ innerh = req.service.get_coll_odata_h(array: @value,
41
113
  template: template)
42
114
 
43
115
  innerj = innerh.to_json
@@ -45,43 +117,69 @@ module Safrano
45
117
  "#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
46
118
  end
47
119
  end
120
+
48
121
  class ResultAsEntity < ResultDefinition
49
- def to_odata_json(result_entity, req)
50
- result_entity.instance_exec do
51
- copy_request_infos(req)
52
- to_odata_json(request: req)
53
- end
122
+
123
+ def self.type_metadata
124
+ @klassmod.type_name
54
125
  end
126
+
127
+
128
+ # wrapper
129
+ # for OData Entity return them directly
130
+ def self.do_execute_func_result(result, _req, apply_query_params=false)
131
+ # note: Sequel entities instances seem to be thread safe, so we can
132
+ # safely add request-dependant data (eg. req.params) there
133
+ apply_query_params ? result : result.inactive_query_params
134
+ end
135
+
55
136
  end
137
+
56
138
  class ResultAsEntityColl < ResultDefinition
57
- def type_metadata
139
+
140
+ def self.type_metadata
58
141
  "Collection(#{@klassmod.type_name})"
59
142
  end
60
-
61
- def to_odata_json(result_dataset, req)
143
+
144
+ # wrapper
145
+ # for OData Entity Collection return them directly
146
+ def self.do_execute_func_result(result, req, apply_query_params=false)
62
147
  coll = Safrano::OData::Collection.new(@klassmod)
148
+ # instance_exec has other instance variables; @values would be nil in the block below
149
+ # need to pass a local copy
150
+ dtset = result
63
151
  coll.instance_exec do
64
- @params = req.params
65
- initialize_dataset(result_dataset)
152
+
153
+ @params = apply_query_params ? req.params : EMPTY_HASH
154
+ initialize_dataset(dtset)
155
+ initialize_uparms
66
156
  end
67
- coll.to_odata_json(request: req)
157
+ coll
68
158
  end
159
+
69
160
  end
161
+
70
162
  class ResultAsPrimitiveType < ResultDefinition
71
- def to_odata_json(result, _req)
163
+ def self.type_metadata
164
+ @klassmod.type_name
165
+ end
166
+
167
+ def to_odata_json(_req)
72
168
  { D => { METAK => { TYPEK => type_metadata },
73
- VALUEK => @klassmod.odata_value(result) } }.to_json
169
+ VALUEK => self.class.klassmod.odata_value(@value) } }.to_json
74
170
  end
75
171
  end
172
+
76
173
  class ResultAsPrimitiveTypeColl < ResultDefinition
77
- def type_metadata
174
+ def self.type_metadata
78
175
  "Collection(#{@klassmod.type_name})"
79
176
  end
80
177
 
81
- def to_odata_json(result, _req)
82
- { D => { METAK => { TYPEK => type_metadata },
83
- RESULTSK => @klassmod.odata_collection(result) } }.to_json
178
+ def to_odata_json(_req)
179
+ { D => { METAK => { TYPEK => self.class.type_metadata },
180
+ RESULTSK => self.class.klassmod.odata_collection(@value) } }.to_json
84
181
  end
182
+
85
183
  end
86
184
  end
87
185
 
@@ -157,11 +255,11 @@ module Safrano
157
255
  end
158
256
 
159
257
  def self.return_as_collection_descriptor
160
- FunctionImport::ResultAsComplexTypeColl.new(self)
258
+ FunctionImport::ResultDefinition.asComplexTypeColl(self)
161
259
  end
162
260
 
163
261
  def self.return_as_instance_descriptor
164
- FunctionImport::ResultAsComplexType.new(self)
262
+ FunctionImport::ResultDefinition.asComplexType(self)
165
263
  end
166
264
 
167
265
  # add metadata xml to the passed REXML schema object
data/lib/odata/entity.rb CHANGED
@@ -125,7 +125,7 @@ module Safrano
125
125
  end
126
126
 
127
127
  def copy_request_infos(req)
128
- @params = req.params
128
+ @params = @inactive_query_params ? EMPTY_HASH : req.params
129
129
  @do_links = req.walker.do_links
130
130
  @uparms = UrlParameters4Single.new(self, @params)
131
131
  end
@@ -151,7 +151,11 @@ module Safrano
151
151
  @uparms.check_all.tap_valid { return odata_get_output(req) }
152
152
  .tap_error { |e| return e.odata_get(req) }
153
153
  end
154
-
154
+ def inactive_query_params
155
+ @inactive_query_params = true
156
+ self # chaining
157
+ end
158
+
155
159
  DELETE_REL_AND_ENTY = lambda do |entity, assoc, parent|
156
160
  Safrano.remove_nav_relation(assoc, parent)
157
161
  entity.destroy(transaction: false)
@@ -21,7 +21,7 @@ module Safrano
21
21
  end
22
22
 
23
23
  def allowed_transitions
24
- [Safrano::TransitionEnd]
24
+ [Safrano::TransitionExecuteFunc]
25
25
  end
26
26
 
27
27
  def input(**parmtypes)
@@ -42,7 +42,13 @@ module Safrano
42
42
  end
43
43
  self
44
44
  end
45
-
45
+
46
+ def auto_query_parameters
47
+ @auto_query_params = true
48
+ self # chaining
49
+ end
50
+ alias auto_query_params auto_query_parameters
51
+
46
52
  def return(klassmod, &proc)
47
53
  raise('Please provide a code block') unless block_given?
48
54
 
@@ -51,7 +57,7 @@ module Safrano
51
57
  else
52
58
  # if it's neither a ComplexType nor a Model-Entity
53
59
  # --> assume it is a Primitive
54
- ResultAsPrimitiveType.new(klassmod)
60
+ ResultDefinition.asPrimitiveType(klassmod)
55
61
  end
56
62
  @proc = proc
57
63
  self
@@ -65,7 +71,8 @@ module Safrano
65
71
  else
66
72
  # if it's neither a ComplexType nor a Modle-Entity
67
73
  # --> assume it is a Primitive
68
- ResultAsPrimitiveTypeColl.new(klassmod)
74
+ # ResultAsPrimitiveTypeColl.new(klassmod)
75
+ ResultDefinition.asPrimitiveTypeColl(klassmod)
69
76
  end
70
77
  @proc = proc
71
78
  self
@@ -135,33 +142,25 @@ module Safrano
135
142
  end if @input
136
143
  funky
137
144
  end
138
-
139
- def with_validated_get(req)
145
+
146
+ def with_transition_validated(req)
140
147
  # initialize_params
148
+ @params = req.params
141
149
  return yield unless (@error = check_url_func_params)
142
150
 
143
- @error.odata_get(req) if @error
144
- end
145
-
146
- def to_odata_json(req)
147
- result = @proc.call(**@funcparams)
148
- @returning.to_odata_json(result, req)
151
+ [nil, :error, @error] if @error
149
152
  end
150
-
151
- def odata_get_output(req)
152
- [200, EMPTY_HASH, [to_odata_json(req)]]
153
- end
154
-
155
- def odata_get(req)
156
- @params = req.params
157
-
158
- with_validated_get(req) do
159
- odata_get_output(req)
153
+
154
+
155
+ def do_execute_func(req)
156
+ with_transition_validated(req) do
157
+ result = @proc.call(**@funcparams)
158
+ [@returning.do_execute_func_result(result, req, @auto_query_params), :run]
160
159
  end
161
160
  end
162
-
163
- def transition_end(_match_result)
164
- Transition::RESULT_END
161
+
162
+ def transition_execute_func(_match_result)
163
+ [self, :run_with_execute_func]
165
164
  end
166
165
  end
167
166
  end
@@ -81,11 +81,11 @@ module Safrano
81
81
  end
82
82
 
83
83
  def return_as_collection_descriptor
84
- Safrano::FunctionImport::ResultAsEntityColl.new(self)
84
+ Safrano::FunctionImport::ResultDefinition.asEntityColl(self)
85
85
  end
86
86
 
87
87
  def return_as_instance_descriptor
88
- Safrano::FunctionImport::ResultAsEntity.new(self)
88
+ Safrano::FunctionImport::ResultDefinition.asEntity(self)
89
89
  end
90
90
 
91
91
  def execute_deferred_iblock
@@ -6,7 +6,7 @@ require_relative 'error'
6
6
  module Safrano
7
7
  # represents a state transition when navigating/parsing the url path
8
8
  # from left to right
9
- class Transition < Regexp
9
+ class Transition
10
10
  attr_accessor :trans
11
11
  attr_accessor :match_result
12
12
  attr_accessor :rgx
@@ -52,8 +52,28 @@ module Safrano
52
52
  ctx.method(@trans).call(@match_result)
53
53
  end
54
54
  end
55
-
55
+
56
+ #Transition that does not move/change the input
57
+ class InplaceTransition < Transition
58
+ def initialize(trans: )
59
+ @trans = trans
60
+ end
61
+ def do_match(str)
62
+ @str = str
63
+ end
64
+ def path_remain
65
+ @str
66
+ end
67
+ def path_done
68
+ EMPTYSTR
69
+ end
70
+ def do_transition(ctx)
71
+ ctx.method(@trans).call(@str)
72
+ end
73
+ end
74
+
56
75
  TransitionEnd = Transition.new('\A(\/?)\z', trans: 'transition_end')
76
+ TransitionExecuteFunc = InplaceTransition.new(trans: 'transition_execute_func')
57
77
  TransitionMetadata = Transition.new('\A(\/\$metadata)(.*)',
58
78
  trans: 'transition_metadata')
59
79
  TransitionBatch = Transition.new('\A(\/\$batch)(.*)',
data/lib/odata/walker.rb CHANGED
@@ -29,18 +29,24 @@ module Safrano
29
29
 
30
30
  # are $links requested ?
31
31
  attr_reader :do_links
32
+
33
+ attr_reader :request
32
34
 
33
35
  NIL_SERVICE_FATAL = 'Walker is called with a nil service'
34
36
  EMPTYSTR = ''
35
37
  SLASH = '/'
36
38
 
37
- def initialize(service, path, content_id_refs = nil)
39
+ def initialize(service, path, request, content_id_refs = nil )
38
40
  raise NIL_SERVICE_FATAL unless service
39
41
 
40
42
  path = URI.decode_www_form_component(path)
41
43
  @context = service
42
44
  @content_id_refs = content_id_refs
43
-
45
+
46
+ # needed because for function import we need access to the url parameters (req.params)
47
+ # who contains the functions params
48
+ @request = request
49
+
44
50
  @contexts = [@context]
45
51
 
46
52
  @path_start = @path_remain = if service
@@ -109,6 +115,16 @@ module Safrano
109
115
  end
110
116
  end
111
117
 
118
+ # execute function import with request parameters
119
+ # input: @context containt the function to exectute,
120
+ # @request.params should normally contain the params
121
+ # result: validate the params for the given function, execute the function and
122
+ # return it's result back into @context,
123
+ # and finaly set status :end (or error if anyting went wrong )
124
+ def do_run_with_execute_func
125
+ @context, @status, @error = @context.do_execute_func(@request)
126
+ end
127
+
112
128
  # little hacks... depending on returned state, set some attributes
113
129
  def state_mappings
114
130
  case @status
@@ -137,6 +153,8 @@ module Safrano
137
153
  # entity reference here and place it in @context
138
154
  when :run_with_content_id
139
155
  do_run_with_content_id
156
+ when :run_with_execute_func
157
+ do_run_with_execute_func
140
158
  end
141
159
 
142
160
  @contexts << @context
@@ -99,6 +99,7 @@ module Safrano
99
99
  def create_odata_walker
100
100
  @env['safrano.walker'] = @walker = Walker.new(@service,
101
101
  path_info,
102
+ self,
102
103
  @content_id_references)
103
104
  end
104
105
 
@@ -137,6 +137,7 @@ module Safrano
137
137
  attr_accessor :relman
138
138
  attr_accessor :complex_types
139
139
  attr_accessor :function_imports
140
+ attr_accessor :function_import_keys
140
141
 
141
142
  # Instance attributes for specialized Version specific Instances
142
143
  attr_accessor :v1
@@ -155,6 +156,7 @@ module Safrano
155
156
  @relman = Safrano::RelationManager.new
156
157
  @complex_types = Set.new
157
158
  @function_imports = {}
159
+ @function_import_keys = []
158
160
  @cmap = {}
159
161
  instance_eval(&block) if block_given?
160
162
  end
@@ -239,6 +241,7 @@ module Safrano
239
241
  other.batch_handler = @batch_handler
240
242
  other.complex_types = @complex_types
241
243
  other.function_imports = @function_imports
244
+ other.function_import_keys = @function_import_keys
242
245
  other
243
246
  end
244
247
 
@@ -322,6 +325,8 @@ module Safrano
322
325
  def function_import(name)
323
326
  funcimp = Safrano::FunctionImport(name)
324
327
  @function_imports[name] = funcimp
328
+ @function_import_keys << name
329
+ set_funcimports_sorted
325
330
  funcimp
326
331
  end
327
332
 
@@ -338,7 +343,11 @@ module Safrano
338
343
  @collections.sort_by! { |klass| klass.entity_set_name.size }.reverse! if @collections
339
344
  @collections
340
345
  end
341
-
346
+
347
+ # need to be sorted by size too
348
+ def set_funcimports_sorted
349
+ @function_import_keys.sort_by!{|k| k.size}.reverse!
350
+ end
342
351
  # to be called at end of publishing block to ensure we get the right names
343
352
  # and additionally build the list of valid attribute path's used
344
353
  # for validation of $orderby or $filter params
@@ -401,7 +410,7 @@ module Safrano
401
410
  end
402
411
 
403
412
  def base_url_func_regexp
404
- @function_imports.keys.join('|')
413
+ @function_import_keys.join('|')
405
414
  end
406
415
 
407
416
  def service
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Safrano
4
- VERSION = '0.5.1'
4
+ VERSION = '0.5.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safrano
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - oz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-27 00:00:00.000000000 Z
11
+ date: 2021-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack