safrano 0.4.0 → 0.4.5
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/Dir/iter.rb +18 -0
- data/lib/core_ext/Hash/transform.rb +21 -0
- data/lib/core_ext/Integer/edm.rb +13 -0
- data/lib/core_ext/REXML/Document/output.rb +16 -0
- data/lib/core_ext/String/convert.rb +25 -0
- data/lib/core_ext/String/edm.rb +13 -0
- data/lib/core_ext/dir.rb +3 -0
- data/lib/core_ext/hash.rb +3 -0
- data/lib/core_ext/integer.rb +3 -0
- data/lib/core_ext/rexml.rb +3 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/odata/attribute.rb +15 -10
- data/lib/odata/batch.rb +15 -13
- data/lib/odata/collection.rb +144 -535
- data/lib/odata/collection_filter.rb +47 -40
- data/lib/odata/collection_media.rb +145 -74
- data/lib/odata/collection_order.rb +50 -37
- data/lib/odata/common_logger.rb +36 -34
- data/lib/odata/complex_type.rb +152 -0
- data/lib/odata/edm/primitive_types.rb +184 -0
- data/lib/odata/entity.rb +151 -197
- data/lib/odata/error.rb +175 -32
- data/lib/odata/expand.rb +126 -0
- data/lib/odata/filter/base.rb +74 -0
- data/lib/odata/filter/error.rb +49 -6
- data/lib/odata/filter/parse.rb +44 -36
- data/lib/odata/filter/sequel.rb +136 -67
- data/lib/odata/filter/sequel_function_adapter.rb +148 -0
- data/lib/odata/filter/token.rb +26 -19
- data/lib/odata/filter/tree.rb +113 -63
- data/lib/odata/function_import.rb +168 -0
- data/lib/odata/model_ext.rb +637 -0
- data/lib/odata/navigation_attribute.rb +44 -61
- data/lib/odata/relations.rb +5 -5
- data/lib/odata/select.rb +54 -0
- data/lib/odata/transition.rb +71 -0
- data/lib/odata/url_parameters.rb +128 -37
- data/lib/odata/walker.rb +19 -11
- data/lib/safrano.rb +17 -37
- data/lib/safrano/contract.rb +143 -0
- data/lib/safrano/core.rb +29 -104
- data/lib/safrano/core_ext.rb +13 -0
- data/lib/safrano/deprecation.rb +73 -0
- data/lib/safrano/multipart.rb +39 -43
- data/lib/safrano/rack_app.rb +68 -67
- data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -2
- data/lib/safrano/request.rb +102 -51
- data/lib/safrano/response.rb +5 -3
- data/lib/safrano/sequel_join_by_paths.rb +2 -2
- data/lib/safrano/service.rb +264 -220
- data/lib/safrano/version.rb +3 -1
- data/lib/sequel/plugins/join_by_paths.rb +17 -29
- metadata +34 -12
data/lib/odata/entity.rb
CHANGED
@@ -1,44 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'rexml/document'
|
3
5
|
require 'safrano.rb'
|
4
|
-
require 'odata/
|
6
|
+
require 'odata/model_ext.rb' # required for self.class.entity_type_name ??
|
5
7
|
require_relative 'navigation_attribute'
|
6
8
|
|
7
|
-
module
|
9
|
+
module Safrano
|
8
10
|
# this will be mixed in the Model classes (subclasses of Sequel Model)
|
9
11
|
module EntityBase
|
10
12
|
attr_reader :params
|
11
|
-
attr_reader :uribase
|
12
13
|
|
13
|
-
include
|
14
|
-
|
14
|
+
include Safrano::NavigationInfo
|
15
|
+
|
15
16
|
# methods related to transitions to next state (cf. walker)
|
16
17
|
module Transitions
|
17
18
|
def allowed_transitions
|
18
|
-
|
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
|
19
|
+
self.class.entity_allowed_transitions
|
38
20
|
end
|
39
21
|
|
40
22
|
def transition_end(_match_result)
|
41
|
-
|
23
|
+
Safrano::Transition::RESULT_END
|
42
24
|
end
|
43
25
|
|
44
26
|
def transition_count(_match_result)
|
@@ -56,8 +38,7 @@ module OData
|
|
56
38
|
|
57
39
|
def transition_attribute(match_result)
|
58
40
|
attrib = match_result[1]
|
59
|
-
|
60
|
-
[OData::Attribute.new(self, attrib), :run]
|
41
|
+
[Safrano::Attribute.new(self, attrib), :run]
|
61
42
|
end
|
62
43
|
|
63
44
|
def transition_nav_collection(match_result)
|
@@ -69,17 +50,26 @@ module OData
|
|
69
50
|
attrib = match_result[1]
|
70
51
|
[get_related_entity(attrib), :run]
|
71
52
|
end
|
53
|
+
|
54
|
+
def transition_invalid_attribute(match_result)
|
55
|
+
invalid_attrib = match_result[1]
|
56
|
+
[nil, :error, Safrano::ErrorNotFoundSegment.new(invalid_attrib)]
|
57
|
+
end
|
72
58
|
end
|
73
59
|
|
74
60
|
include Transitions
|
75
61
|
|
62
|
+
# for testing only?
|
63
|
+
def ==(other)
|
64
|
+
((self.class.type_name == other.class.type_name) and (@values == other.values))
|
65
|
+
end
|
66
|
+
|
76
67
|
def nav_values
|
77
68
|
@nav_values = {}
|
78
69
|
|
79
70
|
self.class.nav_entity_attribs&.each_key do |na_str|
|
80
71
|
@nav_values[na_str.to_sym] = send(na_str)
|
81
72
|
end
|
82
|
-
|
83
73
|
@nav_values
|
84
74
|
end
|
85
75
|
|
@@ -91,50 +81,42 @@ module OData
|
|
91
81
|
@nav_coll
|
92
82
|
end
|
93
83
|
|
94
|
-
def uri
|
95
|
-
"
|
84
|
+
def uri
|
85
|
+
@odata_pk ||= "(#{pk_uri})"
|
86
|
+
"#{self.class.uri}#{@odata_pk}"
|
96
87
|
end
|
88
|
+
|
97
89
|
D = 'd'.freeze
|
98
|
-
|
99
|
-
|
90
|
+
DJ_OPEN = '{"d":'.freeze
|
91
|
+
DJ_CLOSE = '}'.freeze
|
92
|
+
|
100
93
|
# Json formatter for a single entity (probably OData V1/V2 like)
|
101
|
-
def to_odata_json(
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
94
|
+
def to_odata_json(request:)
|
95
|
+
template = self.class.output_template(expand_list: @uparms.expand.template,
|
96
|
+
select: @uparms.select)
|
97
|
+
innerj = request.service.get_entity_odata_h(entity: self,
|
98
|
+
template: template).to_json
|
99
|
+
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
106
100
|
end
|
107
101
|
|
108
102
|
# Json formatter for a single entity reached by navigation $links
|
109
103
|
def to_odata_onelink_json(service:)
|
110
|
-
innerj = service.get_entity_odata_link_h(entity: self
|
111
|
-
|
112
|
-
"#{DJopen}#{innerj}#{DJclose}"
|
104
|
+
innerj = service.get_entity_odata_link_h(entity: self).to_json
|
105
|
+
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
113
106
|
end
|
114
107
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# (and the inverted transformation is in test/client.rb)
|
121
|
-
# will require a more systematic solution some day
|
122
|
-
values_for_odata.transform_values! { |v|
|
123
|
-
case v
|
124
|
-
when Time
|
125
|
-
# try to get back the database time zone and value
|
126
|
-
(v + v.gmt_offset).utc.to_datetime
|
127
|
-
else
|
128
|
-
v
|
129
|
-
end
|
130
|
-
}
|
108
|
+
def selected_values_for_odata(cols)
|
109
|
+
allvals = values_for_odata
|
110
|
+
selvals = {}
|
111
|
+
cols.map(&:to_sym).each { |k| selvals[k] = allvals[k] if allvals.key?(k) }
|
112
|
+
selvals
|
131
113
|
end
|
132
114
|
|
133
115
|
# post paylod expects the new entity in an array
|
134
116
|
def to_odata_post_json(service:)
|
135
117
|
innerj = service.get_coll_odata_h(array: [self],
|
136
|
-
|
137
|
-
"#{
|
118
|
+
template: self.class.default_template).to_json
|
119
|
+
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
138
120
|
end
|
139
121
|
|
140
122
|
def type_name
|
@@ -143,32 +125,37 @@ module OData
|
|
143
125
|
|
144
126
|
def copy_request_infos(req)
|
145
127
|
@params = req.params
|
146
|
-
@uribase = req.uribase
|
147
128
|
@do_links = req.walker.do_links
|
129
|
+
@uparms = UrlParameters4Single.new(self, @params)
|
148
130
|
end
|
149
131
|
|
150
|
-
|
151
|
-
def odata_get(req)
|
152
|
-
copy_request_infos(req)
|
153
|
-
|
132
|
+
def odata_get_output(req)
|
154
133
|
if req.walker.media_value
|
155
134
|
odata_media_value_get(req)
|
156
135
|
elsif req.accept?(APPJSON)
|
136
|
+
# json is default content type so we dont need to specify it here again
|
157
137
|
if req.walker.do_links
|
158
|
-
[200,
|
138
|
+
[200, EMPTY_HASH, [to_odata_onelink_json(service: req.service)]]
|
159
139
|
else
|
160
|
-
[200,
|
140
|
+
[200, EMPTY_HASH, [to_odata_json(request: req)]]
|
161
141
|
end
|
162
142
|
else # TODO: other formats
|
163
143
|
415
|
164
144
|
end
|
165
145
|
end
|
166
|
-
|
146
|
+
|
147
|
+
# Finally Process REST verbs...
|
148
|
+
def odata_get(req)
|
149
|
+
copy_request_infos(req)
|
150
|
+
@uparms.check_all.tap_valid { return odata_get_output(req) }
|
151
|
+
.tap_error { |e| return e.odata_get(req) }
|
152
|
+
end
|
153
|
+
|
167
154
|
DELETE_REL_AND_ENTY = lambda do |entity, assoc, parent|
|
168
|
-
|
155
|
+
Safrano.remove_nav_relation(assoc, parent)
|
169
156
|
entity.destroy(transaction: false)
|
170
157
|
end
|
171
|
-
|
158
|
+
|
172
159
|
def odata_delete_relation_and_entity(req, assoc, parent)
|
173
160
|
if parent
|
174
161
|
if req.in_changeset
|
@@ -182,16 +169,15 @@ module OData
|
|
182
169
|
else
|
183
170
|
destroy(transaction: false)
|
184
171
|
end
|
185
|
-
|
186
|
-
|
172
|
+
rescue StandardError => e
|
173
|
+
raise SequelAdapterError.new(e)
|
187
174
|
end
|
188
|
-
|
175
|
+
|
189
176
|
# TODO: differentiate between POST/PUT/PATCH/MERGE
|
190
177
|
def odata_post(req)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
if req.accept?(APPJSON)
|
178
|
+
if req.walker.media_value
|
179
|
+
odata_media_value_put(req)
|
180
|
+
elsif req.accept?(APPJSON)
|
195
181
|
data.delete('__metadata')
|
196
182
|
|
197
183
|
if req.in_changeset
|
@@ -201,7 +187,7 @@ module OData
|
|
201
187
|
update_fields(data, self.class.data_fields, missing: :skip)
|
202
188
|
end
|
203
189
|
|
204
|
-
[202,
|
190
|
+
[202, EMPTY_HASH, to_odata_post_json(service: req.service)]
|
205
191
|
else # TODO: other formats
|
206
192
|
415
|
207
193
|
end
|
@@ -210,23 +196,20 @@ module OData
|
|
210
196
|
def odata_put(req)
|
211
197
|
if req.walker.media_value
|
212
198
|
odata_media_value_put(req)
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
@uribase = req.uribase
|
217
|
-
data.delete('__metadata')
|
218
|
-
|
219
|
-
if req.in_changeset
|
220
|
-
set_fields(data, self.class.data_fields, missing: :skip)
|
221
|
-
save(transaction: false)
|
222
|
-
else
|
223
|
-
update_fields(data, self.class.data_fields, missing: :skip)
|
224
|
-
end
|
199
|
+
elsif req.accept?(APPJSON)
|
200
|
+
data = JSON.parse(req.body.read)
|
201
|
+
data.delete('__metadata')
|
225
202
|
|
226
|
-
|
227
|
-
|
228
|
-
|
203
|
+
if req.in_changeset
|
204
|
+
set_fields(data, self.class.data_fields, missing: :skip)
|
205
|
+
save(transaction: false)
|
206
|
+
else
|
207
|
+
update_fields(data, self.class.data_fields, missing: :skip)
|
229
208
|
end
|
209
|
+
|
210
|
+
ARY_204_EMPTY_HASH_ARY
|
211
|
+
else # TODO: other formats
|
212
|
+
415
|
230
213
|
end
|
231
214
|
end
|
232
215
|
|
@@ -236,14 +219,12 @@ module OData
|
|
236
219
|
|
237
220
|
# validate payload column names
|
238
221
|
if (invalid = self.class.invalid_hash_data?(data))
|
239
|
-
::
|
240
|
-
return [422,
|
222
|
+
::Safrano::Request::ON_CGST_ERROR.call(req)
|
223
|
+
return [422, EMPTY_HASH, ['Invalid attribute name: ', invalid.to_s]]
|
241
224
|
end
|
242
225
|
# TODO: check values/types
|
243
226
|
|
244
227
|
my_data_fields = self.class.data_fields
|
245
|
-
@uribase = req.uribase
|
246
|
-
# if req.accept?('application/json')
|
247
228
|
|
248
229
|
if req.in_changeset
|
249
230
|
set_fields(data, my_data_fields, missing: :skip)
|
@@ -252,75 +233,17 @@ module OData
|
|
252
233
|
update_fields(data, my_data_fields, missing: :skip)
|
253
234
|
end
|
254
235
|
# patch should return 204 + no content
|
255
|
-
|
236
|
+
ARY_204_EMPTY_HASH_ARY
|
256
237
|
end
|
257
238
|
end
|
258
239
|
|
259
|
-
#
|
260
|
-
#
|
261
|
-
module NavigationRedefinitions
|
262
|
-
def all
|
263
|
-
@child_method.call
|
264
|
-
end
|
265
|
-
|
266
|
-
def count
|
267
|
-
@child_method.call.count
|
268
|
-
end
|
269
|
-
|
270
|
-
def dataset
|
271
|
-
@child_dataset_method.call
|
272
|
-
end
|
273
|
-
|
274
|
-
def navigated_dataset
|
275
|
-
@child_dataset_method.call
|
276
|
-
end
|
277
|
-
|
278
|
-
def each
|
279
|
-
y = @child_method.call
|
280
|
-
y.each { |enty| yield enty }
|
281
|
-
end
|
282
|
-
|
283
|
-
# TODO design... this is not DRY
|
284
|
-
def slug_field
|
285
|
-
superclass.slug_field
|
286
|
-
end
|
287
|
-
|
288
|
-
def type_name
|
289
|
-
superclass.type_name
|
290
|
-
end
|
291
|
-
|
292
|
-
def media_handler
|
293
|
-
superclass.media_handler
|
294
|
-
end
|
295
|
-
|
296
|
-
def to_a
|
297
|
-
y = @child_method.call
|
298
|
-
y.to_a
|
299
|
-
end
|
300
|
-
end
|
301
|
-
# GetRelated that returns a anonymous Class (ie. representing a collection)
|
302
|
-
# subtype of the related object Class ( childklass )
|
240
|
+
# GetRelated that returns a collection object representing
|
241
|
+
# wrapping the related object Class ( childklass )
|
303
242
|
# (...to_many relationship )
|
304
243
|
def get_related(childattrib)
|
305
|
-
|
306
|
-
childklass = self.class.nav_collection_attribs[childattrib]
|
307
|
-
Class.new(childklass) do
|
308
|
-
# this makes use of Sequel's Model relationships; eg this is
|
309
|
-
# 'Race[12].Edition'
|
310
|
-
# where Race[12] would be our self and 'Edition' is the
|
311
|
-
# childattrib(collection)
|
312
|
-
@child_method = parent.method(childattrib.to_sym)
|
313
|
-
@child_dataset_method = parent.method("#{childattrib}_dataset".to_sym)
|
314
|
-
@nav_parent = parent
|
315
|
-
@navattr_reflection = parent.class.association_reflections[childattrib.to_sym]
|
316
|
-
prepare_pk
|
317
|
-
prepare_fields
|
318
|
-
# Now in this anonymous Class we can refine the "all, count and []
|
319
|
-
# methods, to take into account the relationship
|
320
|
-
extend NavigationRedefinitions
|
321
|
-
end
|
244
|
+
Safrano::OData::NavigatedCollection.new(childattrib, self)
|
322
245
|
end
|
323
|
-
|
246
|
+
|
324
247
|
# GetRelatedEntity that returns an single related Entity
|
325
248
|
# (...to_one relationship )
|
326
249
|
def get_related_entity(childattrib)
|
@@ -333,35 +256,35 @@ module OData
|
|
333
256
|
# then we return a Nil... wrapper object. This object then
|
334
257
|
# allows to receive a POST operation that would actually create the nav attribute entity
|
335
258
|
|
336
|
-
ret = method(childattrib.to_sym).call ||
|
337
|
-
|
259
|
+
ret = method(childattrib.to_sym).call || Safrano::NilNavigationAttribute.new
|
260
|
+
|
338
261
|
ret.set_relation_info(self, childattrib)
|
339
|
-
|
262
|
+
|
340
263
|
ret
|
341
264
|
end
|
342
265
|
end
|
343
|
-
# end of module
|
266
|
+
# end of module SafranoEntity
|
344
267
|
module Entity
|
345
268
|
include EntityBase
|
346
269
|
end
|
347
270
|
|
348
271
|
module NonMediaEntity
|
349
272
|
# non media entity metadata for json h
|
350
|
-
def metadata_h
|
351
|
-
{ uri: uri
|
273
|
+
def metadata_h
|
274
|
+
{ uri: uri,
|
352
275
|
type: type_name }
|
353
276
|
end
|
354
277
|
|
355
278
|
def values_for_odata
|
356
|
-
values
|
279
|
+
values
|
357
280
|
end
|
358
281
|
|
359
282
|
def odata_delete(req)
|
360
283
|
if req.accept?(APPJSON)
|
361
|
-
# delete
|
284
|
+
# delete
|
362
285
|
begin
|
363
286
|
odata_delete_relation_and_entity(req, @navattr_reflection, @nav_parent)
|
364
|
-
[200,
|
287
|
+
[200, EMPTY_HASH, [{ 'd' => req.service.get_emptycoll_odata_h }.to_json]]
|
365
288
|
rescue SequelAdapterError => e
|
366
289
|
BadRequestSequelAdapterError.new(e).odata_get(req)
|
367
290
|
end
|
@@ -371,40 +294,65 @@ module OData
|
|
371
294
|
end
|
372
295
|
|
373
296
|
# in case of a non media entity, we have to return an error on $value request
|
374
|
-
def odata_media_value_get(
|
375
|
-
|
297
|
+
def odata_media_value_get(_req)
|
298
|
+
BadRequestNonMediaValue.odata_get
|
376
299
|
end
|
377
300
|
|
378
301
|
# in case of a non media entity, we have to return an error on $value PUT
|
379
|
-
def odata_media_value_put(
|
380
|
-
|
302
|
+
def odata_media_value_put(_req)
|
303
|
+
BadRequestNonMediaValue.odata_get
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
module MappingBeforeOutput
|
308
|
+
# needed for proper datetime output
|
309
|
+
def casted_values(cols = nil)
|
310
|
+
vals = case cols
|
311
|
+
when nil
|
312
|
+
# we need to dup the model values as we need to change it before passing to_json,
|
313
|
+
# but we dont want to interfere with Sequel's owned data
|
314
|
+
# (eg because then in worst case it could happen that we write back changed values to DB)
|
315
|
+
values_for_odata.dup
|
316
|
+
else
|
317
|
+
selected_values_for_odata(cols)
|
318
|
+
end
|
319
|
+
self.class.time_cols.each { |tc| vals[tc] = vals[tc]&.iso8601 if vals.key?(tc) }
|
320
|
+
vals
|
321
|
+
end
|
322
|
+
end
|
323
|
+
module NoMappingBeforeOutput
|
324
|
+
# current model does not have eg. Time fields--> no special mapping, just to_json is fine
|
325
|
+
# --> we can use directly the model.values (values_for_odata) withoud dup'ing it as we dont
|
326
|
+
# need to change it, just output as is
|
327
|
+
def casted_values(cols = nil)
|
328
|
+
case cols
|
329
|
+
when nil
|
330
|
+
values_for_odata
|
331
|
+
else
|
332
|
+
selected_values_for_odata(cols)
|
333
|
+
end
|
381
334
|
end
|
382
335
|
end
|
383
336
|
|
384
337
|
module MediaEntity
|
385
338
|
# media entity metadata for json h
|
386
|
-
def metadata_h
|
387
|
-
{ uri: uri
|
339
|
+
def metadata_h
|
340
|
+
{ uri: uri,
|
388
341
|
type: type_name,
|
389
|
-
media_src: media_src
|
390
|
-
edit_media:
|
342
|
+
media_src: media_src,
|
343
|
+
edit_media: edit_media,
|
391
344
|
content_type: @values[:content_type] }
|
392
345
|
end
|
393
346
|
|
394
|
-
def media_src
|
395
|
-
|
347
|
+
def media_src
|
348
|
+
version = self.class.media_handler.ressource_version(self)
|
349
|
+
"#{uri}/$value?version=#{version}"
|
396
350
|
end
|
397
351
|
|
398
|
-
|
399
|
-
|
400
|
-
type_name
|
352
|
+
def edit_media
|
353
|
+
"#{uri}/$value"
|
401
354
|
end
|
402
355
|
|
403
|
-
# # this is just ModelKlass/pk as a single string
|
404
|
-
# def qualified_media_path_id
|
405
|
-
# "#{self.class}/#{media_path_id}"
|
406
|
-
# end
|
407
|
-
|
408
356
|
def values_for_odata
|
409
357
|
ret = values.dup
|
410
358
|
ret.delete(:content_type)
|
@@ -416,13 +364,12 @@ module OData
|
|
416
364
|
# delete the MR
|
417
365
|
# delegate to the media handler on collection(ie class) level
|
418
366
|
# TODO error handling
|
419
|
-
|
420
|
-
|
421
|
-
self.class.media_handler.odata_delete(request: req, entity: self)
|
367
|
+
|
368
|
+
self.class.media_handler.odata_delete(entity: self)
|
422
369
|
# delete the relation(s) to parent(s) (if any) and then entity
|
423
370
|
odata_delete_relation_and_entity(req, @navattr_reflection, @nav_parent)
|
424
371
|
# result
|
425
|
-
[200,
|
372
|
+
[200, EMPTY_HASH, [{ 'd' => req.service.get_emptycoll_odata_h }.to_json]]
|
426
373
|
else # TODO: other formats
|
427
374
|
415
|
428
375
|
end
|
@@ -438,17 +385,20 @@ module OData
|
|
438
385
|
def odata_media_value_put(req)
|
439
386
|
model = self.class
|
440
387
|
req.with_media_data do |data, mimetype, filename|
|
441
|
-
emdata = { :
|
388
|
+
emdata = { content_type: mimetype }
|
442
389
|
if req.in_changeset
|
443
390
|
set_fields(emdata, model.data_fields, missing: :skip)
|
444
391
|
save(transaction: false)
|
445
392
|
else
|
393
|
+
|
446
394
|
update_fields(emdata, model.data_fields, missing: :skip)
|
395
|
+
|
447
396
|
end
|
448
397
|
model.media_handler.replace_file(data: data,
|
449
398
|
entity: self,
|
450
399
|
filename: filename)
|
451
|
-
|
400
|
+
|
401
|
+
ARY_204_EMPTY_HASH_ARY
|
452
402
|
end
|
453
403
|
end
|
454
404
|
end
|
@@ -463,6 +413,7 @@ module OData
|
|
463
413
|
def media_path_id
|
464
414
|
pk.to_s
|
465
415
|
end
|
416
|
+
|
466
417
|
def media_path_ids
|
467
418
|
[pk]
|
468
419
|
end
|
@@ -472,16 +423,19 @@ module OData
|
|
472
423
|
module EntityMultiPK
|
473
424
|
include Entity
|
474
425
|
def pk_uri
|
475
|
-
|
476
|
-
self.
|
426
|
+
pku = +''
|
427
|
+
self.class.odata_upk_parts.each_with_index { |upart, i|
|
428
|
+
pku = "#{pku}#{upart}#{pk[i]}"
|
429
|
+
}
|
430
|
+
pku
|
477
431
|
end
|
478
432
|
|
479
433
|
def media_path_id
|
480
|
-
|
434
|
+
pk_hash.values.join(SPACE)
|
481
435
|
end
|
482
|
-
|
436
|
+
|
483
437
|
def media_path_ids
|
484
|
-
|
438
|
+
pk_hash.values
|
485
439
|
end
|
486
440
|
end
|
487
441
|
end
|