safrano 0.3.3 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|