safrano 0.4.3 → 0.5.1
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 +8 -4
- data/lib/odata/batch.rb +9 -7
- data/lib/odata/collection.rb +139 -642
- data/lib/odata/collection_filter.rb +18 -42
- data/lib/odata/collection_media.rb +111 -54
- data/lib/odata/collection_order.rb +5 -2
- data/lib/odata/common_logger.rb +2 -0
- data/lib/odata/complex_type.rb +196 -0
- data/lib/odata/edm/primitive_types.rb +184 -0
- data/lib/odata/entity.rb +78 -123
- data/lib/odata/error.rb +170 -37
- data/lib/odata/expand.rb +20 -17
- data/lib/odata/filter/base.rb +9 -1
- data/lib/odata/filter/error.rb +43 -27
- data/lib/odata/filter/parse.rb +39 -25
- data/lib/odata/filter/sequel.rb +112 -56
- data/lib/odata/filter/sequel_function_adapter.rb +50 -49
- data/lib/odata/filter/token.rb +21 -18
- data/lib/odata/filter/tree.rb +78 -44
- data/lib/odata/function_import.rb +168 -0
- data/lib/odata/model_ext.rb +641 -0
- data/lib/odata/navigation_attribute.rb +9 -24
- data/lib/odata/relations.rb +5 -5
- data/lib/odata/select.rb +17 -5
- data/lib/odata/transition.rb +71 -0
- data/lib/odata/url_parameters.rb +100 -24
- data/lib/odata/walker.rb +18 -10
- data/lib/safrano.rb +18 -38
- data/lib/safrano/contract.rb +141 -0
- data/lib/safrano/core.rb +24 -106
- data/lib/safrano/core_ext.rb +13 -0
- data/lib/safrano/deprecation.rb +73 -0
- data/lib/safrano/multipart.rb +29 -24
- data/lib/safrano/rack_app.rb +62 -63
- data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -1
- data/lib/safrano/request.rb +96 -38
- data/lib/safrano/response.rb +4 -2
- data/lib/safrano/sequel_join_by_paths.rb +2 -2
- data/lib/safrano/service.rb +156 -110
- data/lib/safrano/version.rb +3 -1
- data/lib/sequel/plugins/join_by_paths.rb +6 -19
- metadata +30 -11
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Safrano
|
6
|
+
# Type mapping DB --> Edm
|
7
|
+
# TypeMap = {"INTEGER" => "Edm.Int32" , "TEXT" => "Edm.String",
|
8
|
+
# "STRING" => "Edm.String"}
|
9
|
+
# Todo: complete mapping... this is just for the most common ones
|
10
|
+
|
11
|
+
# TODO: use Sequel GENERIC_TYPES: -->
|
12
|
+
# Constants
|
13
|
+
# GENERIC_TYPES = %w'String Integer Float Numeric BigDecimal Date DateTime
|
14
|
+
# Time File TrueClass FalseClass'.freeze
|
15
|
+
# Classes specifying generic types that Sequel will convert to
|
16
|
+
# database-specific types.
|
17
|
+
DB_TYPE_STRING_RGX = /\ACHAR\s*\(\d+\)\z/.freeze
|
18
|
+
|
19
|
+
# used in $metadata
|
20
|
+
# cf. Sequel Database column_schema_default_to_ruby_value
|
21
|
+
# schema_column_type
|
22
|
+
# https://www.odata.org/documentation/odata-version-2-0/overview/
|
23
|
+
def self.default_edm_type(ruby_type:)
|
24
|
+
case ruby_type
|
25
|
+
when :integer
|
26
|
+
'Edm.Int32'
|
27
|
+
when :string
|
28
|
+
'Edm.String'
|
29
|
+
when :date, :datetime,
|
30
|
+
'Edm.DateTime'
|
31
|
+
when :time
|
32
|
+
'Edm.Time'
|
33
|
+
when :boolean
|
34
|
+
'Edm.Boolean'
|
35
|
+
when :float
|
36
|
+
'Edm.Double'
|
37
|
+
when :decimal
|
38
|
+
'Edm.Decimal'
|
39
|
+
when :blob
|
40
|
+
'Edm.Binary'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# use Edm twice so that we can do include Safrano::Edm and then
|
45
|
+
# have Edm::Int32 etc... availabe
|
46
|
+
# and we can have Edm::String different from ::String
|
47
|
+
module Edm
|
48
|
+
module Edm
|
49
|
+
module OutputClassMethods
|
50
|
+
def type_name
|
51
|
+
"Edm.#{name.split('::').last}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def odata_collection(array)
|
55
|
+
array
|
56
|
+
end
|
57
|
+
|
58
|
+
def odata_value(instance)
|
59
|
+
instance
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Null < NilClass
|
64
|
+
extend OutputClassMethods
|
65
|
+
# nil --> null convertion is done by to_json
|
66
|
+
def self.odata_value(instance)
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.convert_from_urlparam(v)
|
71
|
+
return Contract::NOK unless (v == 'null')
|
72
|
+
|
73
|
+
Contract.valid(nil)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Binary is a String with the BINARY encoding
|
78
|
+
class Binary < String
|
79
|
+
extend OutputClassMethods
|
80
|
+
|
81
|
+
def self.convert_from_urlparam(v)
|
82
|
+
Contract.valid(v.dup.force_encoding('BINARY'))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# an object alwys evaluates to
|
87
|
+
# true ([true, anything not false & not nil objs])
|
88
|
+
# or false([nil, false])
|
89
|
+
class Boolean < Object
|
90
|
+
extend OutputClassMethods
|
91
|
+
def Boolean.odata_value(instance)
|
92
|
+
instance ? true : false
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.odata_collection(array)
|
96
|
+
array.map { |v| odata_value(v) }
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.convert_from_urlparam(v)
|
100
|
+
return Contract::NOK unless ['true', 'false'].include?(v)
|
101
|
+
|
102
|
+
Contract.valid(v == 'true')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Bytes are usualy represented as Intger in ruby,
|
107
|
+
# eg.String.bytes --> Array of ints
|
108
|
+
class Byte < Integer
|
109
|
+
extend OutputClassMethods
|
110
|
+
|
111
|
+
def self.convert_from_urlparam(v)
|
112
|
+
return Contract::NOK unless ((bytev = v.to_i) < 256)
|
113
|
+
|
114
|
+
Contract.valid(bytev)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class DateTime < ::DateTime
|
119
|
+
extend OutputClassMethods
|
120
|
+
def DateTime.odata_value(instance)
|
121
|
+
instance.to_datetime
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.odata_collection(array)
|
125
|
+
array.map { |v| odata_value(v) }
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.convert_from_urlparam(v)
|
129
|
+
begin
|
130
|
+
Contract.valid(DateTime.parse(v))
|
131
|
+
rescue
|
132
|
+
return convertion_error(v)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class String < ::String
|
138
|
+
extend OutputClassMethods
|
139
|
+
|
140
|
+
def self.convert_from_urlparam(v)
|
141
|
+
Contract.valid(v)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Int32 < Integer
|
146
|
+
extend OutputClassMethods
|
147
|
+
|
148
|
+
def self.convert_from_urlparam(v)
|
149
|
+
return Contract::NOK unless (ret = number_or_nil(v))
|
150
|
+
|
151
|
+
Contract.valid(ret)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class Int64 < Integer
|
156
|
+
extend OutputClassMethods
|
157
|
+
|
158
|
+
def self.convert_from_urlparam(v)
|
159
|
+
return Contract::NOK unless (ret = number_or_nil(v))
|
160
|
+
|
161
|
+
Contract.valid(ret)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class Double < Float
|
166
|
+
extend OutputClassMethods
|
167
|
+
|
168
|
+
def self.convert_from_urlparam(v)
|
169
|
+
begin
|
170
|
+
Contract.valid(v.to_f)
|
171
|
+
rescue
|
172
|
+
return Contract::NOK
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# include Safrano
|
181
|
+
|
182
|
+
# x = Edm::String.new('xxx')
|
183
|
+
|
184
|
+
# pp x
|
data/lib/odata/entity.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
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
13
|
|
12
|
-
include
|
14
|
+
include Safrano::NavigationInfo
|
13
15
|
|
14
16
|
# methods related to transitions to next state (cf. walker)
|
15
17
|
module Transitions
|
@@ -18,7 +20,7 @@ module OData
|
|
18
20
|
end
|
19
21
|
|
20
22
|
def transition_end(_match_result)
|
21
|
-
|
23
|
+
Safrano::Transition::RESULT_END
|
22
24
|
end
|
23
25
|
|
24
26
|
def transition_count(_match_result)
|
@@ -36,8 +38,7 @@ module OData
|
|
36
38
|
|
37
39
|
def transition_attribute(match_result)
|
38
40
|
attrib = match_result[1]
|
39
|
-
|
40
|
-
[OData::Attribute.new(self, attrib), :run]
|
41
|
+
[Safrano::Attribute.new(self, attrib), :run]
|
41
42
|
end
|
42
43
|
|
43
44
|
def transition_nav_collection(match_result)
|
@@ -49,10 +50,20 @@ module OData
|
|
49
50
|
attrib = match_result[1]
|
50
51
|
[get_related_entity(attrib), :run]
|
51
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
|
52
58
|
end
|
53
59
|
|
54
60
|
include Transitions
|
55
61
|
|
62
|
+
# for testing only?
|
63
|
+
def ==(other)
|
64
|
+
((self.class.type_name == other.class.type_name) and (@values == other.values))
|
65
|
+
end
|
66
|
+
|
56
67
|
def nav_values
|
57
68
|
@nav_values = {}
|
58
69
|
|
@@ -80,10 +91,11 @@ module OData
|
|
80
91
|
DJ_CLOSE = '}'.freeze
|
81
92
|
|
82
93
|
# Json formatter for a single entity (probably OData V1/V2 like)
|
83
|
-
def to_odata_json(
|
84
|
-
template = self.class.output_template(@uparms
|
85
|
-
|
86
|
-
|
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
|
87
99
|
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
88
100
|
end
|
89
101
|
|
@@ -100,10 +112,11 @@ module OData
|
|
100
112
|
selvals
|
101
113
|
end
|
102
114
|
|
103
|
-
# post
|
104
|
-
|
105
|
-
|
106
|
-
|
115
|
+
# some clients wrongly expect post payload with the new entity in an array
|
116
|
+
# TODO quirks array mode !
|
117
|
+
def to_odata_array_json(request:)
|
118
|
+
innerj = request.service.get_coll_odata_h(array: [self],
|
119
|
+
template: self.class.default_template).to_json
|
107
120
|
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
108
121
|
end
|
109
122
|
|
@@ -114,27 +127,33 @@ module OData
|
|
114
127
|
def copy_request_infos(req)
|
115
128
|
@params = req.params
|
116
129
|
@do_links = req.walker.do_links
|
117
|
-
@uparms = UrlParameters4Single.new(@params)
|
130
|
+
@uparms = UrlParameters4Single.new(self, @params)
|
118
131
|
end
|
119
132
|
|
120
|
-
|
121
|
-
def odata_get(req)
|
122
|
-
copy_request_infos(req)
|
133
|
+
def odata_get_output(req)
|
123
134
|
if req.walker.media_value
|
124
135
|
odata_media_value_get(req)
|
125
136
|
elsif req.accept?(APPJSON)
|
137
|
+
# json is default content type so we dont need to specify it here again
|
126
138
|
if req.walker.do_links
|
127
|
-
[200,
|
139
|
+
[200, EMPTY_HASH, [to_odata_onelink_json(service: req.service)]]
|
128
140
|
else
|
129
|
-
[200,
|
141
|
+
[200, EMPTY_HASH, [to_odata_json(request: req)]]
|
130
142
|
end
|
131
143
|
else # TODO: other formats
|
132
144
|
415
|
133
145
|
end
|
134
146
|
end
|
135
147
|
|
148
|
+
# Finally Process REST verbs...
|
149
|
+
def odata_get(req)
|
150
|
+
copy_request_infos(req)
|
151
|
+
@uparms.check_all.tap_valid { return odata_get_output(req) }
|
152
|
+
.tap_error { |e| return e.odata_get(req) }
|
153
|
+
end
|
154
|
+
|
136
155
|
DELETE_REL_AND_ENTY = lambda do |entity, assoc, parent|
|
137
|
-
|
156
|
+
Safrano.remove_nav_relation(assoc, parent)
|
138
157
|
entity.destroy(transaction: false)
|
139
158
|
end
|
140
159
|
|
@@ -201,7 +220,7 @@ module OData
|
|
201
220
|
|
202
221
|
# validate payload column names
|
203
222
|
if (invalid = self.class.invalid_hash_data?(data))
|
204
|
-
::
|
223
|
+
::Safrano::Request::ON_CGST_ERROR.call(req)
|
205
224
|
return [422, EMPTY_HASH, ['Invalid attribute name: ', invalid.to_s]]
|
206
225
|
end
|
207
226
|
# TODO: check values/types
|
@@ -219,89 +238,11 @@ module OData
|
|
219
238
|
end
|
220
239
|
end
|
221
240
|
|
222
|
-
#
|
223
|
-
#
|
224
|
-
module NavigationRedefinitions
|
225
|
-
def all
|
226
|
-
@child_method.call
|
227
|
-
end
|
228
|
-
|
229
|
-
def count
|
230
|
-
@child_method.call.count
|
231
|
-
end
|
232
|
-
|
233
|
-
def dataset
|
234
|
-
@child_dataset_method.call
|
235
|
-
end
|
236
|
-
|
237
|
-
def navigated_dataset
|
238
|
-
@child_dataset_method.call
|
239
|
-
end
|
240
|
-
|
241
|
-
def each
|
242
|
-
y = @child_method.call
|
243
|
-
y.each { |enty| yield enty }
|
244
|
-
end
|
245
|
-
|
246
|
-
# TODO: design... this is not DRY
|
247
|
-
def slug_field
|
248
|
-
superclass.slug_field
|
249
|
-
end
|
250
|
-
|
251
|
-
def type_name
|
252
|
-
superclass.type_name
|
253
|
-
end
|
254
|
-
|
255
|
-
def time_cols
|
256
|
-
superclass.time_cols
|
257
|
-
end
|
258
|
-
|
259
|
-
def media_handler
|
260
|
-
superclass.media_handler
|
261
|
-
end
|
262
|
-
|
263
|
-
def uri
|
264
|
-
superclass.uri
|
265
|
-
end
|
266
|
-
|
267
|
-
def default_template
|
268
|
-
superclass.default_template
|
269
|
-
end
|
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
|
-
|
279
|
-
def to_a
|
280
|
-
y = @child_method.call
|
281
|
-
y.to_a
|
282
|
-
end
|
283
|
-
end
|
284
|
-
# GetRelated that returns a anonymous Class (ie. representing a collection)
|
285
|
-
# subtype of the related object Class ( childklass )
|
241
|
+
# GetRelated that returns a collection object representing
|
242
|
+
# wrapping the related object Class ( childklass )
|
286
243
|
# (...to_many relationship )
|
287
244
|
def get_related(childattrib)
|
288
|
-
|
289
|
-
childklass = self.class.nav_collection_attribs[childattrib]
|
290
|
-
Class.new(childklass) do
|
291
|
-
# this makes use of Sequel's Model relationships; eg this is
|
292
|
-
# 'Race[12].Edition'
|
293
|
-
# where Race[12] would be our self and 'Edition' is the
|
294
|
-
# childattrib(collection)
|
295
|
-
@child_method = parent.method(childattrib.to_sym)
|
296
|
-
@child_dataset_method = parent.method("#{childattrib}_dataset".to_sym)
|
297
|
-
@nav_parent = parent
|
298
|
-
@navattr_reflection = parent.class.association_reflections[childattrib.to_sym]
|
299
|
-
prepare_pk
|
300
|
-
prepare_fields
|
301
|
-
# Now in this anonymous Class we can refine the "all, count and []
|
302
|
-
# methods, to take into account the relationship
|
303
|
-
extend NavigationRedefinitions
|
304
|
-
end
|
245
|
+
Safrano::OData::NavigatedCollection.new(childattrib, self)
|
305
246
|
end
|
306
247
|
|
307
248
|
# GetRelatedEntity that returns an single related Entity
|
@@ -316,14 +257,15 @@ module OData
|
|
316
257
|
# then we return a Nil... wrapper object. This object then
|
317
258
|
# allows to receive a POST operation that would actually create the nav attribute entity
|
318
259
|
|
319
|
-
ret = method(childattrib.to_sym).call ||
|
260
|
+
ret = method(childattrib.to_sym).call || Safrano::NilNavigationAttribute.new
|
320
261
|
|
321
262
|
ret.set_relation_info(self, childattrib)
|
322
263
|
|
323
264
|
ret
|
324
265
|
end
|
325
266
|
end
|
326
|
-
|
267
|
+
|
268
|
+
# end of module SafranoEntity
|
327
269
|
module Entity
|
328
270
|
include EntityBase
|
329
271
|
end
|
@@ -344,7 +286,7 @@ module OData
|
|
344
286
|
# delete
|
345
287
|
begin
|
346
288
|
odata_delete_relation_and_entity(req, @navattr_reflection, @nav_parent)
|
347
|
-
[200,
|
289
|
+
[200, EMPTY_HASH, [{ 'd' => req.service.get_emptycoll_odata_h }.to_json]]
|
348
290
|
rescue SequelAdapterError => e
|
349
291
|
BadRequestSequelAdapterError.new(e).odata_get(req)
|
350
292
|
end
|
@@ -375,7 +317,7 @@ module OData
|
|
375
317
|
values_for_odata.dup
|
376
318
|
else
|
377
319
|
selected_values_for_odata(cols)
|
378
|
-
|
320
|
+
end
|
379
321
|
self.class.time_cols.each { |tc| vals[tc] = vals[tc]&.iso8601 if vals.key?(tc) }
|
380
322
|
vals
|
381
323
|
end
|
@@ -390,7 +332,7 @@ module OData
|
|
390
332
|
values_for_odata
|
391
333
|
else
|
392
334
|
selected_values_for_odata(cols)
|
393
|
-
|
335
|
+
end
|
394
336
|
end
|
395
337
|
end
|
396
338
|
|
@@ -413,16 +355,6 @@ module OData
|
|
413
355
|
"#{uri}/$value"
|
414
356
|
end
|
415
357
|
|
416
|
-
# directory where to put/find the media files for this entity-type
|
417
|
-
def klass_dir
|
418
|
-
type_name
|
419
|
-
end
|
420
|
-
|
421
|
-
# # this is just ModelKlass/pk as a single string
|
422
|
-
# def qualified_media_path_id
|
423
|
-
# "#{self.class}/#{media_path_id}"
|
424
|
-
# end
|
425
|
-
|
426
358
|
def values_for_odata
|
427
359
|
ret = values.dup
|
428
360
|
ret.delete(:content_type)
|
@@ -439,7 +371,7 @@ module OData
|
|
439
371
|
# delete the relation(s) to parent(s) (if any) and then entity
|
440
372
|
odata_delete_relation_and_entity(req, @navattr_reflection, @nav_parent)
|
441
373
|
# result
|
442
|
-
[200,
|
374
|
+
[200, EMPTY_HASH, [{ 'd' => req.service.get_emptycoll_odata_h }.to_json]]
|
443
375
|
else # TODO: other formats
|
444
376
|
415
|
445
377
|
end
|
@@ -460,11 +392,14 @@ module OData
|
|
460
392
|
set_fields(emdata, model.data_fields, missing: :skip)
|
461
393
|
save(transaction: false)
|
462
394
|
else
|
395
|
+
|
463
396
|
update_fields(emdata, model.data_fields, missing: :skip)
|
397
|
+
|
464
398
|
end
|
465
399
|
model.media_handler.replace_file(data: data,
|
466
400
|
entity: self,
|
467
401
|
filename: filename)
|
402
|
+
|
468
403
|
ARY_204_EMPTY_HASH_ARY
|
469
404
|
end
|
470
405
|
end
|
@@ -490,8 +425,11 @@ module OData
|
|
490
425
|
module EntityMultiPK
|
491
426
|
include Entity
|
492
427
|
def pk_uri
|
493
|
-
|
494
|
-
|
428
|
+
pku = +''
|
429
|
+
self.class.odata_upk_parts.each_with_index { |upart, i|
|
430
|
+
pku = "#{pku}#{upart}#{pk[i]}"
|
431
|
+
}
|
432
|
+
pku
|
495
433
|
end
|
496
434
|
|
497
435
|
def media_path_id
|
@@ -502,5 +440,22 @@ module OData
|
|
502
440
|
pk_hash.values
|
503
441
|
end
|
504
442
|
end
|
505
|
-
|
506
|
-
|
443
|
+
|
444
|
+
module EntityCreateStandardOutput
|
445
|
+
# Json formatter for a create entity POST call / Standard version; return as json object
|
446
|
+
def to_odata_create_json(request:)
|
447
|
+
# TODO Perf: reduce method call overhead
|
448
|
+
# we added this redirection for readability and flexibility
|
449
|
+
to_odata_json(request: request)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
module EntityCreateArrayOutput
|
454
|
+
# Json formatter for a create entity POST call Array version
|
455
|
+
def to_odata_create_json(request:)
|
456
|
+
# TODO Perf: reduce method call overhead
|
457
|
+
# we added this redirection for readability and flexibility
|
458
|
+
to_odata_array_json(request: request)
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end # end of Module OData
|