safrano 0.6.7 → 0.7.0
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 +4 -4
- data/lib/core_ext/DateTime/format.rb +1 -1
- data/lib/core_ext/Time/format.rb +2 -2
- data/lib/odata/batch.rb +39 -33
- data/lib/odata/collection.rb +2 -3
- data/lib/odata/collection_media.rb +92 -81
- data/lib/odata/common_logger.rb +19 -2
- data/lib/odata/complex_type.rb +5 -9
- data/lib/odata/edm/primitive_types.rb +53 -64
- data/lib/odata/entity.rb +1 -2
- data/lib/odata/error.rb +3 -3
- data/lib/odata/expand.rb +0 -3
- data/lib/odata/function_import.rb +5 -3
- data/lib/odata/model_ext.rb +21 -26
- data/lib/odata/walker.rb +8 -15
- data/lib/safrano/multipart.rb +39 -28
- data/lib/safrano/rack_app.rb +14 -123
- data/lib/safrano/request.rb +127 -8
- data/lib/safrano/service.rb +3 -3
- data/lib/safrano/version.rb +1 -1
- data/lib/sequel/plugins/join_by_paths.rb +48 -7
- metadata +18 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dda061616194242041f7308c791bc5689fabaf0627f6f5090df3141fe5a34d8e
|
4
|
+
data.tar.gz: bec6791bb225330e1e149bf4166012397f9ffce9df2dbe0be3d7b224c5172212
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7aef08bdf67609d7e836b955e478f4b0a8cd67a08fc6caf0c9e9b263bac3d59086a6da59967cdacf5b3bb328060cbb20b9cdbe70e28fcacb5ce464e63eeb7e59
|
7
|
+
data.tar.gz: 2f65e93d591fc18ca7836a17c61250bc181c1d9aff14bd875e2d2f4803406ca76d577c6cd4c0b5f7e468240ac6d63e9389f7367a5b6cba6bfa298c4c5efd5fcd
|
@@ -49,7 +49,7 @@ module Safrano
|
|
49
49
|
else
|
50
50
|
# same as above with GMT offset in minutes
|
51
51
|
# DateTime offset is Rational ; fraction of hours per Day --> *24*60
|
52
|
-
min_off_s = (min_off = (offset * 60 * 24).to_i)
|
52
|
+
min_off_s = (min_off = (offset * 60 * 24).to_i).positive? ? "+#{min_off}" : min_off.to_s
|
53
53
|
"/Date(#{strftime('%Q')}#{min_off_s})/"
|
54
54
|
end
|
55
55
|
end
|
data/lib/core_ext/Time/format.rb
CHANGED
@@ -57,7 +57,7 @@ module Safrano
|
|
57
57
|
# the json raw data is like this : "HireDate": "\/Date(704678400000)\/"
|
58
58
|
# --> \/\/
|
59
59
|
def to_edm_json
|
60
|
-
if utc? ||
|
60
|
+
if utc? || gmt_offset.zero?
|
61
61
|
# no offset
|
62
62
|
# %s : seconds since unix epoch
|
63
63
|
# %L : milliseconds 000-999
|
@@ -66,7 +66,7 @@ module Safrano
|
|
66
66
|
else
|
67
67
|
# same as above with GMT offset in minutes
|
68
68
|
|
69
|
-
min_off_s = (min_off = gmt_offset / 60)
|
69
|
+
min_off_s = (min_off = gmt_offset / 60).positive? ? "+#{min_off}" : min_off.to_s
|
70
70
|
"/Date(#{strftime('%s%L')}#{min_off_s})/"
|
71
71
|
end
|
72
72
|
end
|
data/lib/odata/batch.rb
CHANGED
@@ -8,54 +8,63 @@ require_relative './common_logger'
|
|
8
8
|
module Safrano
|
9
9
|
# Support for OData multipart $batch Requests
|
10
10
|
class Request
|
11
|
-
def create_batch_app
|
12
|
-
Batch::MyOApp.new(self)
|
13
|
-
end
|
14
|
-
|
15
11
|
def parse_multipart
|
16
12
|
@mimep = MIME::Media::Parser.new
|
17
13
|
@boundary = media_type_params['boundary']
|
18
14
|
@mimep.hook_multipart(media_type, @boundary)
|
19
15
|
@mimep.parse_str(body)
|
20
16
|
end
|
17
|
+
|
18
|
+
# The top-level (full_req) Request is used like
|
19
|
+
# a Rack App
|
20
|
+
# With this method we get the response of part-requests
|
21
|
+
# app.call(env) --> full_req.bach_call(part_req)
|
22
|
+
|
23
|
+
def batch_call(part_req)
|
24
|
+
Safrano::Batch::PartRequest.new(part_req, self).process
|
25
|
+
end
|
26
|
+
|
27
|
+
# needed for changeset transaction
|
28
|
+
def db
|
29
|
+
@service.collections.first.db
|
30
|
+
end
|
21
31
|
end
|
22
32
|
|
23
33
|
module Batch
|
24
|
-
#
|
25
|
-
class
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def initialize(full_req)
|
34
|
+
# Part-Request part of a Batch-Request
|
35
|
+
class PartRequest < Safrano::Request
|
36
|
+
def initialize(part_req, full_req)
|
37
|
+
batch_env(part_req, full_req)
|
38
|
+
@env['HTTP_HOST'] = full_req.env['HTTP_HOST']
|
39
|
+
super(@env, full_req.service_base)
|
31
40
|
@full_req = full_req
|
32
|
-
@
|
41
|
+
@part_req = part_req
|
33
42
|
end
|
34
43
|
|
35
44
|
# redefined for $batch
|
36
45
|
def before
|
37
46
|
headers 'Cache-Control' => 'no-cache'
|
38
|
-
@
|
39
|
-
headers 'DataServiceVersion' => @
|
47
|
+
@service = @full_req.service
|
48
|
+
headers 'DataServiceVersion' => @service.data_service_version
|
40
49
|
end
|
41
50
|
|
42
|
-
def
|
43
|
-
env = batch_env(part_req)
|
44
|
-
env['HTTP_HOST'] = @full_req.env['HTTP_HOST']
|
51
|
+
def process
|
45
52
|
began_at = Rack::Utils.clock_time
|
46
|
-
|
53
|
+
|
47
54
|
@response = Safrano::Response.new
|
48
55
|
|
49
|
-
if part_req.level == 2
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@
|
56
|
+
if @part_req.level == 2
|
57
|
+
@in_changeset = true
|
58
|
+
@content_id = @part_req.content_id
|
59
|
+
@content_id_references = @part_req.content_id_references
|
53
60
|
end
|
54
61
|
|
55
62
|
before
|
63
|
+
|
56
64
|
dispatch
|
57
65
|
|
58
66
|
status, header, body = @response.finish
|
67
|
+
|
59
68
|
# Logging of sub-requests with ODataCommonLogger.
|
60
69
|
# A bit hacky but working
|
61
70
|
# TODO: test ?
|
@@ -80,19 +89,19 @@ module Safrano
|
|
80
89
|
converted_headers
|
81
90
|
end
|
82
91
|
|
83
|
-
def batch_env(mime_req)
|
92
|
+
def batch_env(mime_req, full_req)
|
84
93
|
@env = ::Rack::MockRequest.env_for(mime_req.uri,
|
85
94
|
method: mime_req.http_method,
|
86
95
|
input: mime_req.content)
|
87
96
|
# Logging of sub-requests
|
88
|
-
@env[Rack::RACK_ERRORS] =
|
97
|
+
@env[Rack::RACK_ERRORS] = full_req.env[Rack::RACK_ERRORS]
|
89
98
|
@env.merge! headers_for_env(mime_req.hd)
|
90
99
|
|
91
100
|
@env
|
92
101
|
end
|
93
102
|
end
|
94
103
|
|
95
|
-
#
|
104
|
+
# $batch Handler
|
96
105
|
class HandlerBase
|
97
106
|
TREND = Safrano::Transition.new('', trans: 'transition_end')
|
98
107
|
def allowed_transitions
|
@@ -103,7 +112,8 @@ module Safrano
|
|
103
112
|
Safrano::Transition::RESULT_END
|
104
113
|
end
|
105
114
|
end
|
106
|
-
|
115
|
+
|
116
|
+
# $batch disabled Handler
|
107
117
|
class DisabledHandler < HandlerBase
|
108
118
|
def odata_post(_req)
|
109
119
|
[404, EMPTY_HASH, '$batch is not enabled ']
|
@@ -113,7 +123,8 @@ module Safrano
|
|
113
123
|
[404, EMPTY_HASH, '$batch is not enabled ']
|
114
124
|
end
|
115
125
|
end
|
116
|
-
|
126
|
+
|
127
|
+
# $batch enabled Handler
|
117
128
|
class EnabledHandler < HandlerBase
|
118
129
|
attr_accessor :boundary
|
119
130
|
attr_accessor :mmboundary
|
@@ -121,8 +132,6 @@ module Safrano
|
|
121
132
|
attr_accessor :parts
|
122
133
|
attr_accessor :request
|
123
134
|
|
124
|
-
def initialize; end
|
125
|
-
|
126
135
|
# here we are in the Batch handler object, and this POST should
|
127
136
|
# normally handle a $batch request
|
128
137
|
def odata_post(req)
|
@@ -130,15 +139,12 @@ module Safrano
|
|
130
139
|
|
131
140
|
if @request.media_type == Safrano::MP_MIXED
|
132
141
|
|
133
|
-
batcha = @request.create_batch_app
|
134
142
|
@mult_request = @request.parse_multipart
|
135
143
|
|
136
144
|
@mult_request.prepare_content_id_refs
|
137
|
-
@mult_response = Safrano::Response.new
|
138
145
|
|
139
|
-
|
146
|
+
@mult_request.get_mult_resp(@request)
|
140
147
|
|
141
|
-
[202, resp_hdrs, @mult_response.body[0]]
|
142
148
|
else
|
143
149
|
[415, EMPTY_HASH, 'Unsupported Media Type']
|
144
150
|
end
|
data/lib/odata/collection.rb
CHANGED
@@ -2,49 +2,73 @@
|
|
2
2
|
|
3
3
|
require 'rack'
|
4
4
|
require 'fileutils'
|
5
|
+
require 'tempfile'
|
6
|
+
require 'pathname'
|
5
7
|
require_relative './navigation_attribute'
|
6
8
|
|
7
9
|
module Safrano
|
8
10
|
module Media
|
9
11
|
# base class for Media Handler
|
10
12
|
class Handler
|
11
|
-
def check_before_create(data:,
|
12
|
-
entity:,
|
13
|
-
filename:)
|
13
|
+
def check_before_create(data:, entity:, filename:)
|
14
14
|
Contract::OK
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
18
17
|
# Simple static File/Directory based media store handler
|
19
18
|
# similar to Rack::Static
|
20
19
|
# with a flat directory structure
|
21
20
|
class Static < Handler
|
22
21
|
def initialize(root: nil, mediaklass:)
|
23
|
-
|
24
|
-
@
|
22
|
+
super()
|
23
|
+
@root = Pathname(File.absolute_path(root || Dir.pwd))
|
24
|
+
@files_class = ::Rack.release[0..2] == '2.0' ? ::Rack::File : ::Rack::Files
|
25
25
|
@media_class = mediaklass
|
26
|
-
@media_dir_name = mediaklass.to_s
|
26
|
+
@media_dir_name = Pathname(mediaklass.to_s)
|
27
|
+
@semaphore = Thread::Mutex.new
|
28
|
+
|
27
29
|
register
|
28
30
|
end
|
29
31
|
|
30
32
|
def register
|
31
|
-
@abs_klass_dir =
|
33
|
+
@abs_klass_dir = @root + @media_dir_name
|
34
|
+
@abs_temp_dir = @abs_klass_dir.join('tmp')
|
32
35
|
end
|
33
36
|
|
34
37
|
def create_abs_class_dir
|
35
38
|
FileUtils.makedirs @abs_klass_dir unless Dir.exist?(@abs_klass_dir)
|
36
39
|
end
|
37
40
|
|
41
|
+
def create_abs_temp_dir
|
42
|
+
FileUtils.makedirs @abs_temp_dir unless Dir.exist?(@abs_temp_dir)
|
43
|
+
end
|
44
|
+
|
38
45
|
def finalize
|
39
46
|
create_abs_class_dir
|
47
|
+
create_abs_temp_dir
|
48
|
+
end
|
49
|
+
|
50
|
+
# see also ...
|
51
|
+
# File activesupport/lib/active_support/core_ext/file/atomic.rb, line 21
|
52
|
+
def atomic_write(file_name)
|
53
|
+
Tempfile.open('', @abs_temp_dir) do |temp_file|
|
54
|
+
temp_file.binmode
|
55
|
+
return_val = yield temp_file
|
56
|
+
temp_file.close
|
57
|
+
|
58
|
+
# Overwrite original file with temp file
|
59
|
+
File.rename(temp_file.path, file_name)
|
60
|
+
return_val
|
61
|
+
end
|
40
62
|
end
|
41
63
|
|
42
64
|
# minimal working implementation...
|
43
|
-
# Note:
|
65
|
+
# Note: files_app works relative to @root directory
|
44
66
|
def odata_get(request:, entity:)
|
45
67
|
media_env = request.env.dup
|
46
68
|
media_env['PATH_INFO'] = filename(entity)
|
47
|
-
|
69
|
+
# new app instance for each call for thread safety
|
70
|
+
files_app = @files_class.new(@root)
|
71
|
+
fsret = files_app.call(media_env)
|
48
72
|
if fsret.first == 200
|
49
73
|
# provide own content type as we keep it in the media entity
|
50
74
|
fsret[1]['Content-Type'] = entity.content_type
|
@@ -55,28 +79,23 @@ module Safrano
|
|
55
79
|
# this is relative to @root
|
56
80
|
# eg. Photo/1
|
57
81
|
def media_path(entity)
|
58
|
-
|
82
|
+
@media_dir_name + media_directory(entity)
|
59
83
|
end
|
60
84
|
|
61
85
|
# relative to @root
|
62
86
|
# eg Photo/1/1
|
63
87
|
def filename(entity)
|
64
|
-
|
65
|
-
# simple design: one file per directory, and the directory
|
66
|
-
# contains the media entity-id --> implicit link between the media
|
67
|
-
# entity
|
68
|
-
File.join(media_path(entity), Dir.glob('*').max)
|
69
|
-
end
|
88
|
+
media_path(entity) + ressource_version(entity)
|
70
89
|
end
|
71
90
|
|
72
91
|
# /@root/Photo/1
|
73
92
|
def abs_path(entity)
|
74
|
-
|
93
|
+
@root + media_path(entity)
|
75
94
|
end
|
76
95
|
|
77
96
|
# absolute filename
|
78
97
|
def abs_filename(entity)
|
79
|
-
|
98
|
+
@root + filename(entity)
|
80
99
|
end
|
81
100
|
|
82
101
|
# this is relative to abs_klass_dir(entity) eg to /@root/Photo
|
@@ -86,29 +105,31 @@ module Safrano
|
|
86
105
|
entity.media_path_id
|
87
106
|
end
|
88
107
|
|
89
|
-
|
90
|
-
|
108
|
+
# the same as above but absolute
|
109
|
+
def abs_media_directory(entity)
|
110
|
+
@abs_klass_dir + entity.media_path_id
|
111
|
+
end
|
112
|
+
|
113
|
+
# yields the absolute path of media directory
|
114
|
+
# and ensure the directory exists
|
115
|
+
def with_media_directory(entity)
|
116
|
+
mpi = abs_media_directory(entity)
|
91
117
|
Dir.mkdir mpi unless Dir.exist?(mpi)
|
92
|
-
|
93
|
-
yield
|
94
|
-
end
|
118
|
+
yield Pathname(mpi)
|
95
119
|
end
|
96
120
|
|
97
121
|
def odata_delete(entity:)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
122
|
+
mpi = abs_media_directory(entity)
|
123
|
+
return unless Dir.exist?(mpi)
|
124
|
+
|
125
|
+
mpi.children.each { |oldp| File.delete(oldp) }
|
103
126
|
end
|
104
127
|
|
105
128
|
# Here as well, MVP implementation
|
106
129
|
def save_file(data:, filename:, entity:)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
111
|
-
end
|
130
|
+
with_media_directory(entity) do |d|
|
131
|
+
filename = d.join('1')
|
132
|
+
atomic_write(filename) { |f| IO.copy_stream(data, f) }
|
112
133
|
end
|
113
134
|
end
|
114
135
|
|
@@ -116,24 +137,31 @@ module Safrano
|
|
116
137
|
# after each upload, so that clients get informed about new versions
|
117
138
|
# of the same media ressource
|
118
139
|
def ressource_version(entity)
|
119
|
-
|
120
|
-
in_media_directory(entity) do
|
121
|
-
Dir.glob('*').max
|
122
|
-
end
|
123
|
-
end
|
140
|
+
abs_media_directory(entity).children(false).max.to_s
|
124
141
|
end
|
125
142
|
|
126
|
-
#
|
127
|
-
def replace_file(data:,
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
143
|
+
# Note: add a new Version and remove the previous one
|
144
|
+
def replace_file(data:, entity:)
|
145
|
+
with_media_directory(entity) do |d|
|
146
|
+
tp = Tempfile.open('', @abs_temp_dir) do |temp_file|
|
147
|
+
temp_file.binmode
|
148
|
+
IO.copy_stream(data, temp_file)
|
149
|
+
temp_file.path
|
150
|
+
end
|
151
|
+
|
152
|
+
# picking new filename and the "move" operation must
|
153
|
+
# be protected
|
154
|
+
@semaphore.synchronize do
|
155
|
+
# new filename = "version" + 1
|
156
|
+
v = ressource_version(entity)
|
157
|
+
filename = d + (v.to_i + 1).to_s
|
158
|
+
|
159
|
+
# Move temp file to original target file
|
160
|
+
File.rename(tp, filename)
|
161
|
+
|
162
|
+
# remove the previous version
|
163
|
+
filename = d + v
|
164
|
+
File.delete(filename)
|
137
165
|
end
|
138
166
|
end
|
139
167
|
end
|
@@ -162,42 +190,25 @@ module Safrano
|
|
162
190
|
StaticTree.path_builder(entity.media_path_ids)
|
163
191
|
end
|
164
192
|
|
165
|
-
|
166
|
-
|
193
|
+
# the same as above but absolute
|
194
|
+
def abs_media_directory(entity)
|
195
|
+
@abs_klass_dir + StaticTree.path_builder(entity.media_path_ids)
|
196
|
+
end
|
197
|
+
|
198
|
+
# yields the absolute path of media directory
|
199
|
+
# and ensure the directory exists
|
200
|
+
def with_media_directory(entity)
|
201
|
+
mpi = abs_media_directory(entity)
|
202
|
+
|
167
203
|
FileUtils.makedirs mpi unless Dir.exist?(mpi)
|
168
|
-
|
204
|
+
yield Pathname(mpi)
|
169
205
|
end
|
170
206
|
|
171
207
|
def odata_delete(entity:)
|
172
|
-
|
173
|
-
|
174
|
-
Dir.glob('*').sort.each { |oldf| File.delete(oldf) if File.file?(oldf) }
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
208
|
+
mpi = abs_media_directory(entity)
|
209
|
+
return unless Dir.exist?(mpi)
|
178
210
|
|
179
|
-
|
180
|
-
# def replace_file(data:, filename:, entity:)
|
181
|
-
# Dir.chdir(abs_klass_dir(entity)) do
|
182
|
-
# in_media_directory(entity) do
|
183
|
-
# Dir.glob('*').each { |oldf| File.delete(oldf) if File.file?(oldf) }
|
184
|
-
# File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
185
|
-
# end
|
186
|
-
# end
|
187
|
-
# end
|
188
|
-
# Here as well, MVP implementation
|
189
|
-
def replace_file(data:, filename:, entity:)
|
190
|
-
Dir.chdir(@abs_klass_dir) do
|
191
|
-
in_media_directory(entity) do
|
192
|
-
version = nil
|
193
|
-
Dir.glob('*').sort.each do |oldf|
|
194
|
-
version = oldf
|
195
|
-
File.delete(oldf)
|
196
|
-
end
|
197
|
-
filename = (version.to_i + 1).to_s
|
198
|
-
File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
199
|
-
end
|
200
|
-
end
|
211
|
+
mpi.children.each { |oldp| File.delete(oldp) if File.file?(oldp) }
|
201
212
|
end
|
202
213
|
end
|
203
214
|
end
|
data/lib/odata/common_logger.rb
CHANGED
@@ -13,11 +13,28 @@ module Rack
|
|
13
13
|
MSG_FUNC = case FORMAT.count('%')
|
14
14
|
when 10
|
15
15
|
lambda { |env, length, status, began_at|
|
16
|
-
format(FORMAT,
|
16
|
+
format(FORMAT,
|
17
|
+
env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR'] || '-',
|
18
|
+
env['REMOTE_USER'] || '-',
|
19
|
+
Time.now.strftime('%d/%b/%Y:%H:%M:%S %z'),
|
20
|
+
env[REQUEST_METHOD],
|
21
|
+
env[SCRIPT_NAME] + env[PATH_INFO],
|
22
|
+
env[QUERY_STRING].empty? ? '' : "?#{env[QUERY_STRING]}",
|
23
|
+
env[SERVER_PROTOCOL],
|
24
|
+
status.to_s[0..3], length, Utils.clock_time - began_at)
|
17
25
|
}
|
18
26
|
when 11
|
19
27
|
lambda { |env, length, status, began_at|
|
20
|
-
format(FORMAT,
|
28
|
+
format(FORMAT,
|
29
|
+
env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR'] || '-',
|
30
|
+
env['REMOTE_USER'] || '-',
|
31
|
+
Time.now.strftime('%d/%b/%Y:%H:%M:%S %z'),
|
32
|
+
env[REQUEST_METHOD],
|
33
|
+
env[SCRIPT_NAME],
|
34
|
+
env[PATH_INFO],
|
35
|
+
env[QUERY_STRING].empty? ? '' : "?#{env[QUERY_STRING]}",
|
36
|
+
env[SERVER_PROTOCOL],
|
37
|
+
status.to_s[0..3], length, Utils.clock_time - began_at)
|
21
38
|
}
|
22
39
|
end
|
23
40
|
|
data/lib/odata/complex_type.rb
CHANGED
@@ -92,7 +92,7 @@ module Safrano
|
|
92
92
|
# wrapper
|
93
93
|
# for OData Entity and Collections, return them directly
|
94
94
|
# for others, ie ComplexType, Prims etc, return the ResultDefinition-subclass wrapped result
|
95
|
-
def self.do_execute_func_result(result, _req,
|
95
|
+
def self.do_execute_func_result(result, _req, apply_query_params: false)
|
96
96
|
new(result)
|
97
97
|
end
|
98
98
|
end
|
@@ -129,7 +129,7 @@ module Safrano
|
|
129
129
|
|
130
130
|
# wrapper
|
131
131
|
# for OData Entity return them directly
|
132
|
-
def self.do_execute_func_result(result, _req, apply_query_params
|
132
|
+
def self.do_execute_func_result(result, _req, apply_query_params: false)
|
133
133
|
# note: Sequel entities instances seem to be thread safe, so we can
|
134
134
|
# safely add request-dependant data (eg. req.params) there
|
135
135
|
apply_query_params ? result : result.inactive_query_params
|
@@ -143,7 +143,7 @@ module Safrano
|
|
143
143
|
|
144
144
|
# wrapper
|
145
145
|
# for OData Entity Collection return them directly
|
146
|
-
def self.do_execute_func_result(result, req, apply_query_params
|
146
|
+
def self.do_execute_func_result(result, req, apply_query_params: false)
|
147
147
|
coll = Safrano::OData::Collection.new(@klassmod)
|
148
148
|
# instance_exec has other instance variables; @values would be nil in the block below
|
149
149
|
# need to pass a local copy
|
@@ -241,12 +241,8 @@ module Safrano
|
|
241
241
|
def odata_h
|
242
242
|
ret = { METAK => { TYPEK => self.class.type_name } }
|
243
243
|
|
244
|
-
@values.each do |
|
245
|
-
ret[
|
246
|
-
v.odata_h
|
247
|
-
else
|
248
|
-
v
|
249
|
-
end
|
244
|
+
@values.each do |key, val|
|
245
|
+
ret[key] = val.respond_to?(:odata_h) ? val.odata_h : val
|
250
246
|
end
|
251
247
|
ret
|
252
248
|
end
|