safrano 0.5.1 → 0.5.2

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