safrano 0.3.3 → 0.4.3
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/odata/attribute.rb +9 -8
- data/lib/odata/batch.rb +8 -8
- data/lib/odata/collection.rb +239 -92
- data/lib/odata/collection_filter.rb +40 -9
- data/lib/odata/collection_media.rb +159 -28
- data/lib/odata/collection_order.rb +46 -36
- data/lib/odata/common_logger.rb +37 -12
- data/lib/odata/entity.rb +188 -99
- data/lib/odata/error.rb +60 -12
- data/lib/odata/expand.rb +123 -0
- data/lib/odata/filter/base.rb +66 -0
- data/lib/odata/filter/error.rb +33 -0
- data/lib/odata/filter/parse.rb +6 -12
- data/lib/odata/filter/sequel.rb +42 -29
- data/lib/odata/filter/sequel_function_adapter.rb +147 -0
- data/lib/odata/filter/token.rb +5 -1
- data/lib/odata/filter/tree.rb +45 -29
- data/lib/odata/navigation_attribute.rb +60 -27
- data/lib/odata/relations.rb +2 -2
- data/lib/odata/select.rb +42 -0
- data/lib/odata/url_parameters.rb +51 -36
- data/lib/odata/walker.rb +6 -6
- data/lib/safrano.rb +23 -13
- data/lib/{safrano_core.rb → safrano/core.rb} +12 -4
- data/lib/{multipart.rb → safrano/multipart.rb} +17 -26
- data/lib/{odata_rack_builder.rb → safrano/odata_rack_builder.rb} +0 -1
- data/lib/{rack_app.rb → safrano/rack_app.rb} +12 -10
- data/lib/{request.rb → safrano/request.rb} +8 -14
- data/lib/{response.rb → safrano/response.rb} +1 -2
- data/lib/{sequel_join_by_paths.rb → safrano/sequel_join_by_paths.rb} +1 -1
- data/lib/{service.rb → safrano/service.rb} +162 -131
- data/lib/safrano/version.rb +3 -0
- data/lib/sequel/plugins/join_by_paths.rb +11 -10
- metadata +33 -16
- data/lib/version.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 497482614bfc6a87f8a42b9378a4fc8099ddb5b4f180d747b3717a6952f027b0
|
4
|
+
data.tar.gz: cd5236302671643fd9fbd9bcd200034f305ce7069e5d56ab2db2225dcfb4ce87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4415ca4dc24362f3c179f0cc40f1d73fac6fb7207c0571ef63f1af318fc345e0be8043d8aa1b5ab48e0ead1f5c904dcab07b84061b3ca47f6b7d3c38696d4ae8
|
7
|
+
data.tar.gz: 6646d12aadb3827b43c013a3da6c6223d072e5676f8676eac0db59ad8e3ac80d1a64ceb0f5e87d52bb75e5b556eecda701e2d473c4bdd3b650cb680b6ce19b96
|
data/lib/odata/attribute.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'json'
|
2
|
-
require_relative '../
|
2
|
+
require_relative '../safrano/core.rb'
|
3
3
|
require_relative './entity.rb'
|
4
4
|
|
5
5
|
module OData
|
@@ -13,15 +13,14 @@ module OData
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def value
|
16
|
-
# WARNING
|
17
|
-
# (and the inverted transformation is in test/client.rb)
|
18
|
-
# will require a more systematic solution some day
|
19
|
-
# WARNING 2... this require more work to handle the timezones topci
|
16
|
+
# WARNING ... this require more work to handle the timezones topci
|
20
17
|
# currently it is just set to make some minimal testcase work
|
21
18
|
case (v = @entity.values[@name.to_sym])
|
22
19
|
when Time
|
23
20
|
# try to get back the database time zone and value
|
24
|
-
(v + v.gmt_offset).utc.to_datetime
|
21
|
+
# (v + v.gmt_offset).utc.to_datetime
|
22
|
+
v.iso8601
|
23
|
+
|
25
24
|
else
|
26
25
|
v
|
27
26
|
end
|
@@ -57,9 +56,11 @@ module OData
|
|
57
56
|
[self, :end_with_value]
|
58
57
|
end
|
59
58
|
|
59
|
+
ALLOWED_TRANSITIONS = [Safrano::TransitionEnd,
|
60
|
+
Safrano::TransitionValue].freeze
|
61
|
+
|
60
62
|
def allowed_transitions
|
61
|
-
|
62
|
-
Safrano::TransitionValue]
|
63
|
+
ALLOWED_TRANSITIONS
|
63
64
|
end
|
64
65
|
end
|
65
66
|
include Transitions
|
data/lib/odata/batch.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative '../safrano/rack_app.rb'
|
2
|
+
require_relative '../safrano/core.rb'
|
3
3
|
require 'rack/body_proxy'
|
4
4
|
require_relative './common_logger.rb'
|
5
5
|
|
@@ -59,7 +59,7 @@ module OData
|
|
59
59
|
# TODO: test ?
|
60
60
|
if (logga = @full_req.env['safrano.logger_mw'])
|
61
61
|
logga.batch_log(env, status, header, began_at)
|
62
|
-
# TODO check why/if we need Rack::Utils::HeaderHash.new(header)
|
62
|
+
# TODO: check why/if we need Rack::Utils::HeaderHash.new(header)
|
63
63
|
# and Rack::BodyProxy.new(body) ?
|
64
64
|
end
|
65
65
|
[status, header, body]
|
@@ -71,7 +71,7 @@ module OData
|
|
71
71
|
|
72
72
|
headers.each do |name, value|
|
73
73
|
env_key = name.upcase.tr('-', '_')
|
74
|
-
env_key =
|
74
|
+
env_key = "HTTP_#{env_key}" unless env_key == 'CONTENT_TYPE'
|
75
75
|
converted_headers[env_key] = value
|
76
76
|
end
|
77
77
|
|
@@ -104,11 +104,11 @@ module OData
|
|
104
104
|
# jaune d'oeuf
|
105
105
|
class DisabledHandler < HandlerBase
|
106
106
|
def odata_post(_req)
|
107
|
-
[404,
|
107
|
+
[404, EMPTY_HASH, '$batch is not enabled ']
|
108
108
|
end
|
109
109
|
|
110
110
|
def odata_get(_req)
|
111
|
-
[404,
|
111
|
+
[404, EMPTY_HASH, '$batch is not enabled ']
|
112
112
|
end
|
113
113
|
end
|
114
114
|
# battre le tout
|
@@ -138,12 +138,12 @@ module OData
|
|
138
138
|
|
139
139
|
[202, resp_hdrs, @mult_response.body[0]]
|
140
140
|
else
|
141
|
-
[415,
|
141
|
+
[415, EMPTY_HASH, 'Unsupported Media Type']
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
145
|
def odata_get(_req)
|
146
|
-
[405,
|
146
|
+
[405, EMPTY_HASH, 'You cant GET $batch, POST it ']
|
147
147
|
end
|
148
148
|
end
|
149
149
|
end
|
data/lib/odata/collection.rb
CHANGED
@@ -4,12 +4,14 @@
|
|
4
4
|
|
5
5
|
require 'json'
|
6
6
|
require 'rexml/document'
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
require_relative '../safrano/core.rb'
|
8
|
+
require_relative 'error.rb'
|
9
|
+
require_relative 'collection_filter.rb'
|
10
|
+
require_relative 'collection_order.rb'
|
11
|
+
require_relative 'expand.rb'
|
12
|
+
require_relative 'select.rb'
|
13
|
+
require_relative 'url_parameters.rb'
|
14
|
+
require_relative 'collection_media.rb'
|
13
15
|
|
14
16
|
# small helper method
|
15
17
|
# http://stackoverflow.com/
|
@@ -43,16 +45,25 @@ module OData
|
|
43
45
|
# we will add this to our Model classes with "extend" --> self is the Class
|
44
46
|
module EntityClassBase
|
45
47
|
SINGLE_PK_URL_REGEXP = /\A\(\s*'?([\w\s]+)'?\s*\)(.*)/.freeze
|
46
|
-
ONLY_INTEGER_RGX = /\A[+-]?\d+\z
|
48
|
+
ONLY_INTEGER_RGX = /\A[+-]?\d+\z/.freeze
|
47
49
|
|
48
50
|
attr_reader :nav_collection_url_regexp
|
49
51
|
attr_reader :nav_entity_url_regexp
|
50
52
|
attr_reader :entity_id_url_regexp
|
51
|
-
attr_reader :attrib_paths_url_regexp
|
52
53
|
attr_reader :nav_collection_attribs
|
53
54
|
attr_reader :nav_entity_attribs
|
54
55
|
attr_reader :data_fields
|
55
56
|
attr_reader :inlinecount
|
57
|
+
attr_reader :default_template
|
58
|
+
attr_reader :uri
|
59
|
+
attr_reader :time_cols
|
60
|
+
|
61
|
+
# Sequel associations pointing to this model. Sequel provides association
|
62
|
+
# reflection information on the "from" side. But in some cases
|
63
|
+
# we will need the reverted way
|
64
|
+
# finally not needed and not used yet
|
65
|
+
# attr_accessor :assocs_to
|
66
|
+
|
56
67
|
# set to parent entity in case the collection is a nav.collection
|
57
68
|
# nil otherwise
|
58
69
|
attr_reader :nav_parent
|
@@ -62,9 +73,6 @@ module OData
|
|
62
73
|
# dataset
|
63
74
|
attr_accessor :cx
|
64
75
|
|
65
|
-
# array of the objects --> dataset.to_a
|
66
|
-
attr_accessor :ax
|
67
|
-
|
68
76
|
# url params
|
69
77
|
attr_reader :params
|
70
78
|
|
@@ -84,7 +92,11 @@ module OData
|
|
84
92
|
# the class-name is not the OData Type. In these subclass we redefine "type_name"
|
85
93
|
# thus when we need the Odata type name, we shall use this method instead of just the collection class name
|
86
94
|
def type_name
|
87
|
-
|
95
|
+
@type_name
|
96
|
+
end
|
97
|
+
|
98
|
+
def build_type_name
|
99
|
+
@type_name = to_s
|
88
100
|
end
|
89
101
|
|
90
102
|
# convention: default for entity_set_name is the type name
|
@@ -96,10 +108,15 @@ module OData
|
|
96
108
|
# TODO: automatically reset all attributes?
|
97
109
|
@deferred_iblock = nil
|
98
110
|
@entity_set_name = nil
|
111
|
+
@uri = nil
|
99
112
|
@uparms = nil
|
100
113
|
@params = nil
|
101
|
-
@ax = nil
|
102
114
|
@cx = nil
|
115
|
+
@@time_cols = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def build_uri(uribase)
|
119
|
+
@uri = "#{uribase}/#{entity_set_name}"
|
103
120
|
end
|
104
121
|
|
105
122
|
def execute_deferred_iblock
|
@@ -123,8 +140,7 @@ module OData
|
|
123
140
|
new_entity.save(transaction: false)
|
124
141
|
OData.create_nav_relation(new_entity, assoc, parent)
|
125
142
|
parent.save(transaction: false)
|
126
|
-
|
127
|
-
# not supported
|
143
|
+
# else # not supported
|
128
144
|
end
|
129
145
|
end
|
130
146
|
def odata_create_save_entity_and_rel(req, new_entity, assoc, parent)
|
@@ -155,15 +171,7 @@ module OData
|
|
155
171
|
end
|
156
172
|
|
157
173
|
def odata_get_apply_params
|
158
|
-
|
159
|
-
@cx = @uparms.apply_to_dataset(@cx)
|
160
|
-
rescue OData::Filter::Parser::ErrorWrongColumnName
|
161
|
-
@error = BadRequestFilterParseError
|
162
|
-
return
|
163
|
-
rescue OData::Filter::Parser::ErrorFunctionArgumentType
|
164
|
-
@error = BadRequestFilterParseError
|
165
|
-
return
|
166
|
-
end
|
174
|
+
@cx = @uparms.apply_to_dataset(@cx)
|
167
175
|
odata_get_inlinecount_w_sequel
|
168
176
|
|
169
177
|
@cx = @cx.offset(@params['$skip']) if @params['$skip']
|
@@ -192,9 +200,7 @@ module OData
|
|
192
200
|
def check_u_p_inlinecount
|
193
201
|
return unless (icp = @params['$inlinecount'])
|
194
202
|
|
195
|
-
unless (icp == 'allpages') || (icp == 'none')
|
196
|
-
return BadRequestInlineCountParamError
|
197
|
-
end
|
203
|
+
return BadRequestInlineCountParamError unless (icp == 'allpages') || (icp == 'none')
|
198
204
|
|
199
205
|
nil
|
200
206
|
end
|
@@ -207,26 +213,27 @@ module OData
|
|
207
213
|
@uparms.check_order
|
208
214
|
end
|
209
215
|
|
216
|
+
def check_u_p_expand
|
217
|
+
@uparms.check_expand
|
218
|
+
end
|
219
|
+
|
210
220
|
def build_attribute_path_list
|
211
221
|
@attribute_path_list = attribute_path_list
|
212
|
-
@attrib_paths_url_regexp = @attribute_path_list.join('|')
|
213
222
|
end
|
214
223
|
|
215
|
-
|
224
|
+
MAX_DEPTH = 6
|
225
|
+
def attribute_path_list(depth = 0)
|
226
|
+
ret = @columns.map(&:to_s)
|
216
227
|
# break circles
|
217
|
-
return
|
228
|
+
return ret if depth > MAX_DEPTH
|
218
229
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
ret.concat(k.attribute_path_list(nodes).map { |kc| "#{a}/#{kc}" })
|
224
|
-
end
|
230
|
+
depth += 1
|
231
|
+
|
232
|
+
@nav_entity_attribs&.each do |a, k|
|
233
|
+
ret.concat(k.attribute_path_list(depth).map { |kc| "#{a}/#{kc}" })
|
225
234
|
end
|
226
|
-
|
227
|
-
|
228
|
-
ret.concat(k.attribute_path_list(nodes).map { |kc| "#{a}/#{kc}" })
|
229
|
-
end
|
235
|
+
@nav_collection_attribs&.each do |a, k|
|
236
|
+
ret.concat(k.attribute_path_list(depth).map { |kc| "#{a}/#{kc}" })
|
230
237
|
end
|
231
238
|
ret
|
232
239
|
end
|
@@ -235,20 +242,18 @@ module OData
|
|
235
242
|
return nil unless @params
|
236
243
|
|
237
244
|
check_u_p_top || check_u_p_skip || check_u_p_orderby ||
|
238
|
-
check_u_p_filter || check_u_p_inlinecount
|
245
|
+
check_u_p_filter || check_u_p_expand || check_u_p_inlinecount
|
239
246
|
end
|
240
247
|
|
241
248
|
def initialize_dataset
|
242
249
|
@cx = self
|
243
|
-
@ax = nil
|
244
250
|
@cx = navigated_dataset if @cx.nav_parent
|
245
251
|
@model = if @cx.respond_to? :model
|
246
252
|
@cx.model
|
247
253
|
else
|
248
254
|
@cx
|
249
255
|
end
|
250
|
-
@
|
251
|
-
@uparms = UrlParameters.new(@jh, @params)
|
256
|
+
@uparms = UrlParameters4Coll.new(@model, @params)
|
252
257
|
end
|
253
258
|
|
254
259
|
# finally return the requested output according to format, options etc
|
@@ -268,15 +273,31 @@ module OData
|
|
268
273
|
end
|
269
274
|
end
|
270
275
|
|
276
|
+
# validation/error handling methods.
|
277
|
+
# normal processing is done in the passed proc
|
278
|
+
|
279
|
+
def with_validated_get(req)
|
280
|
+
begin
|
281
|
+
initialize_dataset
|
282
|
+
return yield unless (@error = check_url_params)
|
283
|
+
rescue OData::Filter::Parser::ErrorWrongColumnName
|
284
|
+
@error = BadRequestFilterParseError
|
285
|
+
rescue OData::Filter::Parser::ErrorFunctionArgumentType
|
286
|
+
@error = BadRequestFilterParseError
|
287
|
+
rescue OData::Filter::FunctionNotImplemented => e
|
288
|
+
@error = e.odata_error
|
289
|
+
rescue OData::Filter::Parser::ErrorInvalidFunction => e
|
290
|
+
@error = e.odata_error
|
291
|
+
end
|
292
|
+
|
293
|
+
@error.odata_get(req) if @error
|
294
|
+
end
|
295
|
+
|
271
296
|
# on model class level we return the collection
|
272
297
|
def odata_get(req)
|
273
298
|
@params = req.params
|
274
|
-
@uribase = req.uribase
|
275
|
-
initialize_dataset
|
276
299
|
|
277
|
-
|
278
|
-
@error.odata_get(req)
|
279
|
-
else
|
300
|
+
with_validated_get(req) do
|
280
301
|
odata_get_apply_params
|
281
302
|
odata_get_output(req)
|
282
303
|
end
|
@@ -288,7 +309,11 @@ module OData
|
|
288
309
|
|
289
310
|
# add metadata xml to the passed REXML schema object
|
290
311
|
def add_metadata_rexml(schema)
|
291
|
-
enty =
|
312
|
+
enty = if @media_handler
|
313
|
+
schema.add_element('EntityType', 'Name' => to_s, 'HasStream' => 'true')
|
314
|
+
else
|
315
|
+
schema.add_element('EntityType', 'Name' => to_s)
|
316
|
+
end
|
292
317
|
# with their properties
|
293
318
|
db_schema.each do |pnam, prop|
|
294
319
|
if prop[:primary_key] == true
|
@@ -304,47 +329,119 @@ module OData
|
|
304
329
|
end
|
305
330
|
|
306
331
|
# metadata REXML data for a single Nav attribute
|
307
|
-
def metadata_nav_rexml_attribs(assoc,
|
332
|
+
def metadata_nav_rexml_attribs(assoc, to_klass, relman, xnamespace)
|
308
333
|
from = type_name
|
309
|
-
to =
|
334
|
+
to = to_klass.type_name
|
310
335
|
relman.get_metadata_xml_attribs(from,
|
311
336
|
to,
|
312
|
-
association_reflection(assoc)[:type],
|
313
|
-
xnamespace
|
337
|
+
association_reflection(assoc.to_sym)[:type],
|
338
|
+
xnamespace,
|
339
|
+
assoc)
|
314
340
|
end
|
315
341
|
|
316
342
|
# and their Nav attributes == Sequel Model association
|
317
|
-
def add_metadata_navs_rexml(schema_enty,
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
343
|
+
def add_metadata_navs_rexml(schema_enty, relman, xnamespace)
|
344
|
+
@nav_entity_attribs&.each do |ne, klass|
|
345
|
+
nattr = metadata_nav_rexml_attribs(ne,
|
346
|
+
klass,
|
347
|
+
relman,
|
348
|
+
xnamespace)
|
349
|
+
schema_enty.add_element('NavigationProperty', nattr)
|
350
|
+
end
|
323
351
|
|
324
|
-
|
352
|
+
@nav_collection_attribs&.each do |nc, klass|
|
353
|
+
nattr = metadata_nav_rexml_attribs(nc,
|
354
|
+
klass,
|
355
|
+
relman,
|
356
|
+
xnamespace)
|
357
|
+
schema_enty.add_element('NavigationProperty', nattr)
|
325
358
|
end
|
326
359
|
end
|
327
360
|
|
328
361
|
D = 'd'.freeze
|
329
|
-
|
330
|
-
|
362
|
+
DJ_OPEN = '{"d":'.freeze
|
363
|
+
DJ_CLOSE = '}'.freeze
|
331
364
|
def to_odata_links_json(service:)
|
332
|
-
innerj = service.get_coll_odata_links_h(array:
|
333
|
-
uribase: @uribase,
|
365
|
+
innerj = service.get_coll_odata_links_h(array: @cx.all,
|
334
366
|
icount: @inlinecount).to_json
|
335
|
-
"#{
|
367
|
+
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
336
368
|
end
|
337
369
|
|
338
|
-
def
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
370
|
+
# def output_template(expand: nil, select: nil)
|
371
|
+
def output_template(uparms)
|
372
|
+
# output_template_deep(expand_list: expand_list, select: select)
|
373
|
+
output_template_deep(expand_list: uparms.expand.template, select: uparms.select)
|
374
|
+
end
|
375
|
+
|
376
|
+
# Recursive
|
377
|
+
def output_template_deep(expand_list:, select: OData::SelectBase::ALL)
|
378
|
+
return default_template if expand_list.empty? && select.all_props?
|
379
|
+
|
380
|
+
template = {}
|
381
|
+
expand_e = {}
|
382
|
+
expand_c = {}
|
383
|
+
deferr = []
|
384
|
+
|
385
|
+
# 1. handle non-navigation properties, only consider $select
|
386
|
+
# 2. handle navigations properties, need to check $select and $expand
|
387
|
+
if select.all_props?
|
388
|
+
template[:all_values] = EMPTYH
|
389
|
+
# include all nav attributes -->
|
390
|
+
@nav_entity_attribs&.each do |attr, klass|
|
391
|
+
if expand_list.key?(attr)
|
392
|
+
expand_e[attr] = klass.output_template_deep(expand_list: expand_list[attr])
|
393
|
+
else
|
394
|
+
deferr << attr
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
@nav_collection_attribs&.each do |attr, klass|
|
399
|
+
if expand_list.key?(attr)
|
400
|
+
expand_c[attr] = klass.output_template_deep(expand_list: expand_list[attr])
|
401
|
+
else
|
402
|
+
deferr << attr
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
else
|
407
|
+
template[:selected_vals] = @columns.map(&:to_s) & select.props
|
408
|
+
# include only selected nav attribs-->need additional intersection step
|
409
|
+
if @nav_entity_attribs
|
410
|
+
selected_nav_e = @nav_entity_attribs.keys & select.props
|
411
|
+
|
412
|
+
selected_nav_e&.each do |attr|
|
413
|
+
if expand_list.key?(attr)
|
414
|
+
klass = @nav_entity_attribs[attr]
|
415
|
+
expand_e[attr] = klass.output_template_deep(expand_list: expand_list[attr])
|
416
|
+
else
|
417
|
+
deferr << attr
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
if @nav_collection_attribs
|
422
|
+
selected_nav_c = @nav_collection_attribs.keys & select.props
|
423
|
+
selected_nav_c&.each do |attr|
|
424
|
+
if expand_list.key?(attr)
|
425
|
+
klass = @nav_collection_attribs[attr]
|
426
|
+
expand_c[attr] = klass.output_template_deep(expand_list: expand_list[attr])
|
427
|
+
else
|
428
|
+
deferr << attr
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
template[:expand_e] = expand_e if expand_e
|
434
|
+
template[:expand_c] = expand_c if expand_c
|
435
|
+
template[:deferr] = deferr if deferr
|
436
|
+
template
|
344
437
|
end
|
345
438
|
|
346
|
-
def
|
347
|
-
|
439
|
+
def to_odata_json(service:)
|
440
|
+
template = output_template(@uparms)
|
441
|
+
innerj = service.get_coll_odata_h(array: @cx.all,
|
442
|
+
template: template,
|
443
|
+
icount: @inlinecount).to_json
|
444
|
+
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
348
445
|
end
|
349
446
|
|
350
447
|
# this functionally similar to the Sequel Rels (many_to_one etc)
|
@@ -356,9 +453,8 @@ module OData
|
|
356
453
|
assoc = all_association_reflections.find do |a|
|
357
454
|
a[:name] == assoc_symb && a[:model] == self
|
358
455
|
end
|
359
|
-
|
360
|
-
|
361
|
-
end
|
456
|
+
|
457
|
+
raise OData::API::ModelAssociationNameError.new(self, assoc_symb) unless assoc
|
362
458
|
|
363
459
|
attr_class = assoc[:class_name].constantize
|
364
460
|
lattr_name_str = (attr_name_str || assoc_symb.to_s)
|
@@ -373,9 +469,8 @@ module OData
|
|
373
469
|
assoc = all_association_reflections.find do |a|
|
374
470
|
a[:name] == assoc_symb && a[:model] == self
|
375
471
|
end
|
376
|
-
|
377
|
-
|
378
|
-
end
|
472
|
+
|
473
|
+
raise OData::API::ModelAssociationNameError.new(self, assoc_symb) unless assoc
|
379
474
|
|
380
475
|
attr_class = assoc[:class_name].constantize
|
381
476
|
lattr_name_str = (attr_name_str || assoc_symb.to_s)
|
@@ -383,10 +478,38 @@ module OData
|
|
383
478
|
@nav_entity_url_regexp = @nav_entity_attribs.keys.join('|')
|
384
479
|
end
|
385
480
|
|
481
|
+
EMPTYH = {}.freeze
|
482
|
+
|
483
|
+
def build_default_template
|
484
|
+
template = { all_values: EMPTYH }
|
485
|
+
if @nav_entity_attribs || @nav_collection_attribs
|
486
|
+
template[:deferr] = (@nav_entity_attribs&.keys || []) + (@nav_collection_attribs&.keys || EMPTY_ARRAY)
|
487
|
+
end
|
488
|
+
template
|
489
|
+
end
|
386
490
|
# old names...
|
387
491
|
# alias_method :add_nav_prop_collection, :addNavCollectionAttrib
|
388
492
|
# alias_method :add_nav_prop_single, :addNavEntityAttrib
|
389
493
|
|
494
|
+
def finalize_publishing
|
495
|
+
build_type_name
|
496
|
+
|
497
|
+
# finalize media handler
|
498
|
+
@media_handler.register(self) if @media_handler
|
499
|
+
|
500
|
+
# build default output template structure
|
501
|
+
@default_template = build_default_template
|
502
|
+
|
503
|
+
# Time columns
|
504
|
+
@time_cols = db_schema.select { |c, v| v[:type] == :datetime }.map { |c, v| c }
|
505
|
+
|
506
|
+
# and finally build the path list and allowed tr's
|
507
|
+
build_attribute_path_list
|
508
|
+
|
509
|
+
build_allowed_transitions
|
510
|
+
build_entity_allowed_transitions
|
511
|
+
end
|
512
|
+
|
390
513
|
def prepare_pk
|
391
514
|
if primary_key.is_a? Array
|
392
515
|
@pk_names = []
|
@@ -435,11 +558,7 @@ module OData
|
|
435
558
|
def check_odata_val_type(val, type)
|
436
559
|
case type
|
437
560
|
when :integer
|
438
|
-
|
439
|
-
[true, Integer(val)]
|
440
|
-
else
|
441
|
-
[false, val]
|
442
|
-
end
|
561
|
+
val =~ ONLY_INTEGER_RGX ? [true, Integer(val)] : [false, val]
|
443
562
|
when :string
|
444
563
|
[true, val]
|
445
564
|
else
|
@@ -479,10 +598,38 @@ module OData
|
|
479
598
|
end
|
480
599
|
|
481
600
|
def allowed_transitions
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
601
|
+
@allowed_transitions
|
602
|
+
end
|
603
|
+
|
604
|
+
def entity_allowed_transitions
|
605
|
+
@entity_allowed_transitions
|
606
|
+
end
|
607
|
+
|
608
|
+
def build_allowed_transitions
|
609
|
+
@allowed_transitions = [Safrano::TransitionEnd,
|
610
|
+
Safrano::TransitionCount,
|
611
|
+
Safrano::Transition.new(entity_id_url_regexp,
|
612
|
+
trans: 'transition_id')].freeze
|
613
|
+
end
|
614
|
+
|
615
|
+
def build_entity_allowed_transitions
|
616
|
+
@entity_allowed_transitions = [
|
617
|
+
Safrano::TransitionEnd,
|
618
|
+
Safrano::TransitionCount,
|
619
|
+
Safrano::TransitionLinks,
|
620
|
+
Safrano::TransitionValue,
|
621
|
+
Safrano::Transition.new(transition_attribute_regexp, trans: 'transition_attribute')
|
622
|
+
]
|
623
|
+
if (ncurgx = @nav_collection_url_regexp)
|
624
|
+
@entity_allowed_transitions <<
|
625
|
+
Safrano::Transition.new(%r{\A/(#{ncurgx})(.*)\z}, trans: 'transition_nav_collection')
|
626
|
+
end
|
627
|
+
if (neurgx = @nav_entity_url_regexp)
|
628
|
+
@entity_allowed_transitions <<
|
629
|
+
Safrano::Transition.new(%r{\A/(#{neurgx})(.*)\z}, trans: 'transition_nav_entity')
|
630
|
+
end
|
631
|
+
@entity_allowed_transitions.freeze
|
632
|
+
@entity_allowed_transitions
|
486
633
|
end
|
487
634
|
end
|
488
635
|
include Transitions
|
@@ -500,7 +647,7 @@ module OData
|
|
500
647
|
md.shift # remove first element which is the whole match
|
501
648
|
mdc = []
|
502
649
|
error = false
|
503
|
-
primary_key.each_with_index
|
650
|
+
primary_key.each_with_index do |pk, i|
|
504
651
|
ck, casted = check_odata_val_type(md[i], db_schema[pk][:type])
|
505
652
|
if ck
|
506
653
|
mdc << casted
|
@@ -508,7 +655,7 @@ module OData
|
|
508
655
|
error = true
|
509
656
|
break
|
510
657
|
end
|
511
|
-
|
658
|
+
end
|
512
659
|
if error
|
513
660
|
[false, md]
|
514
661
|
else
|
@@ -538,7 +685,7 @@ module OData
|
|
538
685
|
# validate payload column names
|
539
686
|
if (invalid = invalid_hash_data?(data))
|
540
687
|
::OData::Request::ON_CGST_ERROR.call(req)
|
541
|
-
return [422,
|
688
|
+
return [422, EMPTY_HASH, ['Invalid attribute name: ', invalid.to_s]]
|
542
689
|
end
|
543
690
|
|
544
691
|
if req.accept?(APPJSON)
|