safrano 0.4.4 → 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/odata/attribute.rb +2 -2
- data/lib/odata/collection.rb +6 -3
- data/lib/odata/collection_filter.rb +5 -5
- data/lib/odata/collection_media.rb +27 -3
- data/lib/odata/complex_type.rb +10 -10
- data/lib/odata/error.rb +9 -0
- data/lib/odata/filter/base.rb +5 -0
- data/lib/odata/filter/parse.rb +6 -0
- data/lib/odata/filter/sequel.rb +15 -0
- data/lib/odata/filter/token.rb +13 -10
- data/lib/odata/filter/tree.rb +4 -4
- data/lib/odata/function_import.rb +12 -10
- data/lib/odata/model_ext.rb +19 -0
- data/lib/odata/walker.rb +3 -3
- data/lib/safrano/contract.rb +5 -5
- data/lib/safrano/core.rb +12 -12
- data/lib/safrano/deprecation.rb +5 -5
- data/lib/safrano/multipart.rb +2 -2
- data/lib/safrano/rack_app.rb +1 -1
- data/lib/safrano/request.rb +2 -2
- data/lib/safrano/service.rb +18 -19
- data/lib/safrano/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f2ebcbe8cf374526aa263f93515e2424753898ad878904d6d5d087bc6317c4e
|
4
|
+
data.tar.gz: 48190a71451991630bd48347c3cf2acb701423bb58266fa736b0cf7a5b6f3bdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70ef7d275b566269879e4cd37f019958cd354504d1dede907adcf4d078e47361412997c69eec2c403317013ef5ac8019315aeb8cc89715906f2f12bd59e885dc
|
7
|
+
data.tar.gz: 1ff899294284a5e9531187533938e14ae304c1344131177dff1612037ca32e6a27dfcd72b367dd705afbb5cd5b9d38e9c11dc7b8f7766d470c2ac4c45686372b
|
data/lib/odata/attribute.rb
CHANGED
@@ -53,7 +53,7 @@ module Safrano
|
|
53
53
|
# methods related to transitions to next state (cf. walker)
|
54
54
|
module Transitions
|
55
55
|
def transition_end(_match_result)
|
56
|
-
|
56
|
+
Transition::RESULT_END
|
57
57
|
end
|
58
58
|
|
59
59
|
def transition_value(_match_result)
|
@@ -64,7 +64,7 @@ module Safrano
|
|
64
64
|
Safrano::TransitionValue].freeze
|
65
65
|
|
66
66
|
def allowed_transitions
|
67
|
-
ALLOWED_TRANSITIONS
|
67
|
+
Transitions::ALLOWED_TRANSITIONS
|
68
68
|
end
|
69
69
|
end
|
70
70
|
include Transitions
|
data/lib/odata/collection.rb
CHANGED
@@ -77,17 +77,20 @@ module Safrano
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
D = 'd'
|
81
|
-
DJ_OPEN = '{"d":'
|
82
|
-
DJ_CLOSE = '}'
|
80
|
+
D = 'd'
|
81
|
+
DJ_OPEN = '{"d":'
|
82
|
+
DJ_CLOSE = '}'
|
83
83
|
EMPTYH = {}.freeze
|
84
84
|
|
85
85
|
def to_odata_json(request:)
|
86
86
|
template = @modelk.output_template(expand_list: @uparms.expand.template,
|
87
87
|
select: @uparms.select)
|
88
|
+
# TODO: Error handling if database contains binary BLOB data that cant be
|
89
|
+
# interpreted as UTF-8 then JSON will fail here
|
88
90
|
innerj = request.service.get_coll_odata_h(array: @cx.all,
|
89
91
|
template: template,
|
90
92
|
icount: @inlinecount).to_json
|
93
|
+
|
91
94
|
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
92
95
|
end
|
93
96
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'odata/error
|
3
|
+
require 'odata/error'
|
4
4
|
|
5
|
-
require_relative 'filter/parse
|
6
|
-
require_relative 'filter/sequel
|
5
|
+
require_relative 'filter/parse'
|
6
|
+
require_relative 'filter/sequel'
|
7
7
|
|
8
8
|
# filter base class and subclass in our OData namespace
|
9
9
|
module Safrano
|
@@ -45,12 +45,12 @@ module Safrano
|
|
45
45
|
# the join-helper is shared by the order-by object and was potentially already
|
46
46
|
# partly built on order-by object creation.
|
47
47
|
def finalize(jh)
|
48
|
-
@filtexpr = @ast.if_valid
|
48
|
+
@filtexpr = @ast.if_valid { |ast| ast.sequel_expr(jh) }
|
49
49
|
end
|
50
50
|
|
51
51
|
def apply_to_dataset(dtcx)
|
52
52
|
# normally finalize is called before, and thus @filtexpr is set
|
53
|
-
@filtexpr.map_result!
|
53
|
+
@filtexpr.map_result! { |f| dtcx.where(f) }
|
54
54
|
end
|
55
55
|
|
56
56
|
# Note: this is really only *parse* error, ie the error encounterd while
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'rack'
|
4
4
|
require 'fileutils'
|
5
|
-
require_relative './navigation_attribute
|
5
|
+
require_relative './navigation_attribute'
|
6
6
|
|
7
7
|
module Safrano
|
8
8
|
module Media
|
@@ -24,9 +24,16 @@ module Safrano
|
|
24
24
|
|
25
25
|
def register
|
26
26
|
@abs_klass_dir = File.absolute_path(@media_dir_name, @root)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_abs_class_dir
|
27
30
|
FileUtils.makedirs @abs_klass_dir unless Dir.exist?(@abs_klass_dir)
|
28
31
|
end
|
29
32
|
|
33
|
+
def finalize
|
34
|
+
create_abs_class_dir
|
35
|
+
end
|
36
|
+
|
30
37
|
# minimal working implementation...
|
31
38
|
# Note: @file_server works relative to @root directory
|
32
39
|
def odata_get(request:, entity:)
|
@@ -53,7 +60,7 @@ module Safrano
|
|
53
60
|
# simple design: one file per directory, and the directory
|
54
61
|
# contains the media entity-id --> implicit link between the media
|
55
62
|
# entity
|
56
|
-
File.join(media_path(entity), Dir.glob('*').
|
63
|
+
File.join(media_path(entity), Dir.glob('*').min)
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
@@ -62,6 +69,11 @@ module Safrano
|
|
62
69
|
File.absolute_path(media_path(entity), @root)
|
63
70
|
end
|
64
71
|
|
72
|
+
# absolute filename
|
73
|
+
def abs_filename(entity)
|
74
|
+
File.absolute_path(filename(entity), @root)
|
75
|
+
end
|
76
|
+
|
65
77
|
# this is relative to abs_klass_dir(entity) eg to /@root/Photo
|
66
78
|
# simplest implementation is media_directory = entity.media_path_id
|
67
79
|
# --> we get a 1 level depth flat directory structure
|
@@ -101,7 +113,7 @@ module Safrano
|
|
101
113
|
def ressource_version(entity)
|
102
114
|
Dir.chdir(@abs_klass_dir) do
|
103
115
|
in_media_directory(entity) do
|
104
|
-
Dir.glob('*').
|
116
|
+
Dir.glob('*').max
|
105
117
|
end
|
106
118
|
end
|
107
119
|
end
|
@@ -201,9 +213,14 @@ module Safrano
|
|
201
213
|
@media_handler = Safrano::Media::Static.new(mediaklass: self)
|
202
214
|
end
|
203
215
|
|
216
|
+
def finalize_media
|
217
|
+
@media_handler.finalize
|
218
|
+
end
|
219
|
+
|
204
220
|
def use(klass, args)
|
205
221
|
args[:mediaklass] = self
|
206
222
|
@media_handler = klass.new(**args)
|
223
|
+
@media_handler.create_abs_class_dir
|
207
224
|
end
|
208
225
|
|
209
226
|
# API method for setting the model field mapped to SLUG on upload
|
@@ -261,9 +278,16 @@ module Safrano
|
|
261
278
|
req.register_content_id_ref(new_entity)
|
262
279
|
new_entity.copy_request_infos(req)
|
263
280
|
|
281
|
+
# call before_create_media hook
|
282
|
+
new_entity.before_create_media if new_entity.respond_to? :before_create_media
|
283
|
+
|
264
284
|
media_handler.save_file(data: data,
|
265
285
|
entity: new_entity,
|
266
286
|
filename: filename)
|
287
|
+
|
288
|
+
# call after_create_media hook
|
289
|
+
new_entity.after_create_media if new_entity.respond_to? :after_create_media
|
290
|
+
|
267
291
|
# json is default content type so we dont need to specify it here again
|
268
292
|
[201, EMPTY_HASH, new_entity.to_odata_post_json(service: req.service)]
|
269
293
|
else # TODO: other formats
|
data/lib/odata/complex_type.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
module Safrano
|
4
4
|
module FunctionImport
|
5
5
|
class ResultDefinition
|
6
|
-
D = 'd'
|
7
|
-
DJ_OPEN = '{"d":'
|
8
|
-
DJ_CLOSE = '}'
|
9
|
-
METAK = '__metadata'
|
10
|
-
TYPEK = 'type'
|
11
|
-
VALUEK = 'value'
|
12
|
-
RESULTSK = 'results'
|
13
|
-
COLLECTION = 'Collection'
|
6
|
+
D = 'd'
|
7
|
+
DJ_OPEN = '{"d":'
|
8
|
+
DJ_CLOSE = '}'
|
9
|
+
METAK = '__metadata'
|
10
|
+
TYPEK = 'type'
|
11
|
+
VALUEK = 'value'
|
12
|
+
RESULTSK = 'results'
|
13
|
+
COLLECTION = 'Collection'
|
14
14
|
|
15
15
|
def initialize(klassmod)
|
16
16
|
@klassmod = klassmod
|
@@ -97,8 +97,8 @@ module Safrano
|
|
97
97
|
def initialize
|
98
98
|
@values = {}
|
99
99
|
end
|
100
|
-
METAK = '__metadata'
|
101
|
-
TYPEK = 'type'
|
100
|
+
METAK = '__metadata'
|
101
|
+
TYPEK = 'type'
|
102
102
|
|
103
103
|
def odata_h
|
104
104
|
ret = { METAK => { TYPEK => self.class.type_name } }
|
data/lib/odata/error.rb
CHANGED
@@ -37,6 +37,15 @@ module Safrano
|
|
37
37
|
super(msg, symbname)
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
# duplicate attribute name
|
42
|
+
class ModelDuplicateAttributeError < NameError
|
43
|
+
def initialize(klass, symb)
|
44
|
+
symbname = symb.to_s
|
45
|
+
msg = "There is already an attribute :#{symbname} defined in class #{klass}"
|
46
|
+
super(msg, symbname)
|
47
|
+
end
|
48
|
+
end
|
40
49
|
end
|
41
50
|
|
42
51
|
# base module for HTTP errors, when used as a Error Class
|
data/lib/odata/filter/base.rb
CHANGED
@@ -57,6 +57,11 @@ module Safrano
|
|
57
57
|
class Literal < Leave
|
58
58
|
end
|
59
59
|
|
60
|
+
# Null Literal is unquoted null word
|
61
|
+
class NullLiteral < Literal
|
62
|
+
LEUQES = nil
|
63
|
+
end
|
64
|
+
|
60
65
|
# Qualit (qualified lits) are words separated by /
|
61
66
|
# path/path/path/attrib
|
62
67
|
class Qualit < Literal
|
data/lib/odata/filter/parse.rb
CHANGED
@@ -151,6 +151,12 @@ module Safrano
|
|
151
151
|
grow_at_cursor(Literal.new(tok))
|
152
152
|
end
|
153
153
|
|
154
|
+
when :NullLiteral
|
155
|
+
with_accepted(tok, typ) do
|
156
|
+
@cursor.update_state(tok, typ)
|
157
|
+
grow_at_cursor(NullLiteral.new(tok))
|
158
|
+
end
|
159
|
+
|
154
160
|
when :Qualit
|
155
161
|
with_accepted(tok, typ) do
|
156
162
|
@cursor.update_state(tok, typ)
|
data/lib/odata/filter/sequel.rb
CHANGED
@@ -183,6 +183,13 @@ module Safrano
|
|
183
183
|
end
|
184
184
|
Contract.collect_result!(@children[0].leuqes(jh),
|
185
185
|
@children[1].leuqes(jh)) do |c0, c1|
|
186
|
+
if c1 == NullLiteral::LEUQES
|
187
|
+
if @value == :eq
|
188
|
+
leuqes_op = :IS
|
189
|
+
elsif @value == :ne
|
190
|
+
leuqes_op = :'IS NOT'
|
191
|
+
end
|
192
|
+
end
|
186
193
|
Sequel::SQL::BooleanExpression.new(leuqes_op, c0, c1)
|
187
194
|
end
|
188
195
|
end
|
@@ -262,6 +269,14 @@ module Safrano
|
|
262
269
|
end
|
263
270
|
end
|
264
271
|
|
272
|
+
# Null
|
273
|
+
class NullLiteral
|
274
|
+
def leuqes(jh)
|
275
|
+
# Sequel's representation of NULL
|
276
|
+
success LEUQES
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
265
280
|
# Qualit (qualified lits) are words separated by /
|
266
281
|
class Qualit
|
267
282
|
def leuqes(jh)
|
data/lib/odata/filter/token.rb
CHANGED
@@ -10,13 +10,14 @@ module Safrano
|
|
10
10
|
day hour minute month second year
|
11
11
|
round floor ceiling].freeze
|
12
12
|
FUNCRGX = FUNCNAMES.join('|')
|
13
|
+
NULLRGX = 'null|NULL|Null'
|
13
14
|
QSTRINGRGX = /'((?:[^']|(?:'{2}))*)'/.freeze
|
14
15
|
BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'
|
15
16
|
BINOARITHM = '[aA][dD][dD]|[sS][uU][bB]|[mM][uU][lL]|[dD][iI][vV]|[mM][oO][dD]'
|
16
|
-
NOTRGX = 'not|NOT'
|
17
|
+
NOTRGX = 'not|NOT|Not'
|
17
18
|
FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?'
|
18
19
|
QUALITRGX = '\w+(?:\/\w+)+'
|
19
|
-
RGX = /(#{FUNCRGX})|([\(\),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
|
20
|
+
RGX = /(#{FUNCRGX})|(#{NULLRGX})|([\(\),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
|
20
21
|
|
21
22
|
def each_typed_token(inp)
|
22
23
|
typ = nil
|
@@ -34,27 +35,29 @@ module Safrano
|
|
34
35
|
when 0
|
35
36
|
:FuncTree
|
36
37
|
when 1
|
38
|
+
:NullLiteral
|
39
|
+
when 2
|
37
40
|
case found
|
38
41
|
when '(', ')'
|
39
42
|
:Delimiter
|
40
43
|
when ','
|
41
44
|
:Separator
|
42
45
|
end
|
43
|
-
when 2
|
44
|
-
:BinopBool
|
45
46
|
when 3
|
46
|
-
:
|
47
|
+
:BinopBool
|
47
48
|
when 4
|
48
|
-
:
|
49
|
+
:BinopArithm
|
49
50
|
when 5
|
50
|
-
:
|
51
|
+
:UnopTree
|
51
52
|
when 6
|
52
|
-
:
|
53
|
+
:QString
|
53
54
|
when 7
|
54
|
-
:
|
55
|
+
:FPNumber
|
55
56
|
when 8
|
56
|
-
:
|
57
|
+
:Qualit
|
57
58
|
when 9
|
59
|
+
:Literal
|
60
|
+
when 10
|
58
61
|
:unmatchedQuote
|
59
62
|
end
|
60
63
|
yield found, typ
|
data/lib/odata/filter/tree.rb
CHANGED
@@ -65,7 +65,7 @@ module Safrano
|
|
65
65
|
# nil is considered as accepted, otherwise non-nil=the error
|
66
66
|
def accept?(tok, typ)
|
67
67
|
case typ
|
68
|
-
when :Literal, :Qualit, :QString, :FuncTree, :ArgTree,
|
68
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :ArgTree,
|
69
69
|
:UnopTree, :FPNumber
|
70
70
|
nil
|
71
71
|
when :Delimiter
|
@@ -232,7 +232,7 @@ module Safrano
|
|
232
232
|
|
233
233
|
def update_state(_tok, typ)
|
234
234
|
case typ
|
235
|
-
when :Literal, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm, :UnopTree, :FPNumber
|
235
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm, :UnopTree, :FPNumber
|
236
236
|
@state = :closed
|
237
237
|
end
|
238
238
|
end
|
@@ -297,7 +297,7 @@ module Safrano
|
|
297
297
|
@state = :closed
|
298
298
|
when :Separator
|
299
299
|
@state = :sep
|
300
|
-
when :Literal, :Qualit, :QString, :FuncTree, :FPNumber
|
300
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
|
301
301
|
@state = :val
|
302
302
|
end
|
303
303
|
end
|
@@ -327,7 +327,7 @@ module Safrano
|
|
327
327
|
elsif @state == :sep
|
328
328
|
Parser::ErrorInvalidToken.new(tok, typ, self)
|
329
329
|
end
|
330
|
-
when :Literal, :Qualit, :QString, :FuncTree, :FPNumber
|
330
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
|
331
331
|
if (@state == :open) || (@state == :sep)
|
332
332
|
if @parent.arity_full?(@children.size)
|
333
333
|
Parser::ErrorInvalidArity.new(tok, typ, self)
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
require_relative 'complex_type'
|
4
4
|
require_relative 'edm/primitive_types'
|
5
|
+
require_relative 'transition'
|
6
|
+
|
5
7
|
module Safrano
|
6
|
-
def
|
8
|
+
def self.FunctionImport(name)
|
7
9
|
FunctionImport::Function.new(name)
|
8
10
|
end
|
9
11
|
|
@@ -24,7 +26,7 @@ module Safrano
|
|
24
26
|
|
25
27
|
def input(**parmtypes)
|
26
28
|
@input = {}
|
27
|
-
parmtypes.each
|
29
|
+
parmtypes.each do |k, t|
|
28
30
|
@input[k] = case t.name
|
29
31
|
when 'Integer'
|
30
32
|
Safrano::Edm::Edm::Int32
|
@@ -37,7 +39,7 @@ module Safrano
|
|
37
39
|
else
|
38
40
|
t
|
39
41
|
end
|
40
|
-
|
42
|
+
end
|
41
43
|
self
|
42
44
|
end
|
43
45
|
|
@@ -91,20 +93,20 @@ module Safrano
|
|
91
93
|
return nil unless @input # anything to check ?
|
92
94
|
|
93
95
|
# do we have all parameters provided ?
|
94
|
-
check_missing_params.tap_error
|
96
|
+
check_missing_params.tap_error { |error| return error }
|
95
97
|
# ==> all params were provided
|
96
98
|
|
97
99
|
# now we shall check the content and type of the parameters
|
98
|
-
@input.each
|
100
|
+
@input.each do |ksym, typ|
|
99
101
|
typ.convert_from_urlparam(v = @params[ksym.to_s])
|
100
102
|
.tap_valid do |retval|
|
101
103
|
@funcparams[ksym] = retval
|
102
104
|
end
|
103
|
-
.tap_error do
|
105
|
+
.tap_error do
|
104
106
|
# return is really needed here, or we end up returning nil below
|
105
107
|
return parameter_convertion_error(ksym, typ, v)
|
106
108
|
end
|
107
|
-
|
109
|
+
end
|
108
110
|
nil
|
109
111
|
end
|
110
112
|
|
@@ -125,12 +127,12 @@ module Safrano
|
|
125
127
|
# EntitySet= @entity_set ,
|
126
128
|
'ReturnType' => @returning.type_metadata,
|
127
129
|
'm:HttpMethod' => @http_method)
|
128
|
-
@input.each
|
130
|
+
@input.each do |iname, type|
|
129
131
|
funky.add_element('Parameter',
|
130
132
|
'Name' => iname.to_s,
|
131
133
|
'Type' => type.type_name,
|
132
134
|
'Mode' => 'In')
|
133
|
-
|
135
|
+
end if @input
|
134
136
|
funky
|
135
137
|
end
|
136
138
|
|
@@ -159,7 +161,7 @@ module Safrano
|
|
159
161
|
end
|
160
162
|
|
161
163
|
def transition_end(_match_result)
|
162
|
-
|
164
|
+
Transition::RESULT_END
|
163
165
|
end
|
164
166
|
end
|
165
167
|
end
|
data/lib/odata/model_ext.rb
CHANGED
@@ -299,6 +299,14 @@ module Safrano
|
|
299
299
|
|
300
300
|
attr_class = assoc[:class_name].constantize
|
301
301
|
lattr_name_str = (attr_name_str || assoc_symb.to_s)
|
302
|
+
|
303
|
+
# check duplicate attributes names
|
304
|
+
raise Safrano::API::ModelDuplicateAttributeError.new(self, lattr_name_str) if @columns.include? lattr_name_str.to_sym
|
305
|
+
|
306
|
+
if @nav_entity_attribs_keys
|
307
|
+
raise Safrano::API::ModelDuplicateAttributeError.new(self, lattr_name_str) if @nav_entity_attribs_keys.include? lattr_name_str
|
308
|
+
end
|
309
|
+
|
302
310
|
@nav_collection_attribs[lattr_name_str] = attr_class
|
303
311
|
@nav_collection_attribs_keys << lattr_name_str
|
304
312
|
@nav_collection_url_regexp = @nav_collection_attribs_keys.join('|')
|
@@ -317,6 +325,14 @@ module Safrano
|
|
317
325
|
|
318
326
|
attr_class = assoc[:class_name].constantize
|
319
327
|
lattr_name_str = (attr_name_str || assoc_symb.to_s)
|
328
|
+
|
329
|
+
# check duplicate attributes names
|
330
|
+
raise Safrano::API::ModelDuplicateAttributeError.new(self, lattr_name_str) if @columns.include? lattr_name_str.to_sym
|
331
|
+
|
332
|
+
if @nav_collection_attribs_keys
|
333
|
+
raise Safrano::API::ModelDuplicateAttributeError.new(self, lattr_name_str) if @nav_collection_attribs_keys.include? lattr_name_str
|
334
|
+
end
|
335
|
+
|
320
336
|
@nav_entity_attribs[lattr_name_str] = attr_class
|
321
337
|
@nav_entity_attribs_keys << lattr_name_str
|
322
338
|
@nav_entity_url_regexp = @nav_entity_attribs_keys.join('|')
|
@@ -352,6 +368,9 @@ module Safrano
|
|
352
368
|
|
353
369
|
build_allowed_transitions
|
354
370
|
build_entity_allowed_transitions
|
371
|
+
|
372
|
+
# for media
|
373
|
+
finalize_media if self.respond_to? :finalize_media
|
355
374
|
end
|
356
375
|
|
357
376
|
KEYPRED_URL_REGEXP = /\A\(\s*'?([\w=,'\s]+)'?\s*\)(.*)/.freeze
|
data/lib/odata/walker.rb
CHANGED
@@ -30,9 +30,9 @@ module Safrano
|
|
30
30
|
# are $links requested ?
|
31
31
|
attr_reader :do_links
|
32
32
|
|
33
|
-
NIL_SERVICE_FATAL = 'Walker is called with a nil service'
|
34
|
-
EMPTYSTR = ''
|
35
|
-
SLASH = '/'
|
33
|
+
NIL_SERVICE_FATAL = 'Walker is called with a nil service'
|
34
|
+
EMPTYSTR = ''
|
35
|
+
SLASH = '/'
|
36
36
|
|
37
37
|
def initialize(service, path, content_id_refs = nil)
|
38
38
|
raise NIL_SERVICE_FATAL unless service
|
data/lib/safrano/contract.rb
CHANGED
@@ -106,11 +106,11 @@ module Safrano
|
|
106
106
|
end
|
107
107
|
end # class Valid
|
108
108
|
|
109
|
-
def
|
109
|
+
def self.valid(result)
|
110
110
|
Contract::Valid.new(result)
|
111
111
|
end
|
112
112
|
|
113
|
-
def
|
113
|
+
def self.and(*contracts)
|
114
114
|
# pick the first error if any
|
115
115
|
if (ff = contracts.find(&:error))
|
116
116
|
return ff
|
@@ -118,18 +118,18 @@ module Safrano
|
|
118
118
|
|
119
119
|
# return a new one with @result = list of the contracts's results
|
120
120
|
# usually this then be reduced again with #collect_result! or # #if_valid_collect methods
|
121
|
-
|
121
|
+
valid(contracts.map(&:result))
|
122
122
|
end
|
123
123
|
|
124
124
|
# shortcut for Contract.and(*contracts).collect_result!
|
125
|
-
def
|
125
|
+
def self.collect_result!(*contracts)
|
126
126
|
# pick the first error if any
|
127
127
|
if (ff = contracts.find(&:error))
|
128
128
|
return ff
|
129
129
|
end
|
130
130
|
|
131
131
|
# return a new one with @result = yield(*list of the contracts's results)
|
132
|
-
|
132
|
+
valid(yield(*contracts.map(&:result)))
|
133
133
|
end
|
134
134
|
|
135
135
|
# generic success return, when return value does not matter
|
data/lib/safrano/core.rb
CHANGED
@@ -6,22 +6,22 @@ module Safrano
|
|
6
6
|
EMPTY_ARRAY = [].freeze
|
7
7
|
EMPTY_HASH = {}.freeze
|
8
8
|
EMPTY_HASH_IN_ARY = [EMPTY_HASH].freeze
|
9
|
-
EMPTY_STRING = ''
|
9
|
+
EMPTY_STRING = ''
|
10
10
|
ARY_204_EMPTY_HASH_ARY = [204, EMPTY_HASH, EMPTY_ARRAY].freeze
|
11
|
-
SPACE = ' '
|
12
|
-
COMMA = ','
|
11
|
+
SPACE = ' '
|
12
|
+
COMMA = ','
|
13
13
|
|
14
14
|
# some prominent constants... probably already defined elsewhere eg in Rack
|
15
15
|
# but lets KISS
|
16
|
-
CONTENT_TYPE = 'Content-Type'
|
17
|
-
CTT_TYPE_LC = 'content-type'
|
18
|
-
TEXTPLAIN_UTF8 = 'text/plain;charset=utf-8'
|
19
|
-
APPJSON = 'application/json'
|
20
|
-
APPXML = 'application/xml'
|
21
|
-
MP_MIXED = 'multipart/mixed'
|
22
|
-
APPXML_UTF8 = 'application/xml;charset=utf-8'
|
23
|
-
APPATOMXML_UTF8 = 'application/atomsvc+xml;charset=utf-8'
|
24
|
-
APPJSON_UTF8 = 'application/json;charset=utf-8'
|
16
|
+
CONTENT_TYPE = 'Content-Type'
|
17
|
+
CTT_TYPE_LC = 'content-type'
|
18
|
+
TEXTPLAIN_UTF8 = 'text/plain;charset=utf-8'
|
19
|
+
APPJSON = 'application/json'
|
20
|
+
APPXML = 'application/xml'
|
21
|
+
MP_MIXED = 'multipart/mixed'
|
22
|
+
APPXML_UTF8 = 'application/xml;charset=utf-8'
|
23
|
+
APPATOMXML_UTF8 = 'application/atomsvc+xml;charset=utf-8'
|
24
|
+
APPJSON_UTF8 = 'application/json;charset=utf-8'
|
25
25
|
|
26
26
|
CT_JSON = { CONTENT_TYPE => APPJSON_UTF8 }.freeze
|
27
27
|
CT_TEXT = { CONTENT_TYPE => TEXTPLAIN_UTF8 }.freeze
|
data/lib/safrano/deprecation.rb
CHANGED
@@ -22,7 +22,7 @@ module Safrano
|
|
22
22
|
module Deprecation
|
23
23
|
@backtrace_filter = false
|
24
24
|
@output = $stderr
|
25
|
-
@prefix =
|
25
|
+
@prefix = 'SAFRANO DEPRECATION WARNING: '
|
26
26
|
|
27
27
|
class << self
|
28
28
|
# How to filter backtraces. +false+ does not include backtraces, +true+ includes
|
@@ -64,10 +64,10 @@ module Safrano
|
|
64
64
|
# otherwise do nothing as the ruby implementation does not support constant deprecation.
|
65
65
|
def self.deprecate_constant(mod, constant)
|
66
66
|
# :nocov:
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
return unless RUBY_VERSION > '2.3'
|
68
|
+
|
69
|
+
# :nocov:
|
70
|
+
mod.deprecate_constant(constant)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
data/lib/safrano/multipart.rb
CHANGED
@@ -110,11 +110,11 @@ module MIME
|
|
110
110
|
@target.ct = @target_ct
|
111
111
|
@state = :bmp
|
112
112
|
end
|
113
|
-
MPS = 'multipart/'
|
113
|
+
MPS = 'multipart/'
|
114
114
|
MP_RGX1 = %r{^(digest|mixed);\s*boundary="(.*)"}.freeze
|
115
115
|
MP_RGX2 = %r{^(digest|mixed);\s*boundary=(.*)}.freeze
|
116
116
|
# APP_HTTP_RGX = %r{^application/http}.freeze
|
117
|
-
APP_HTTP = 'application/http'
|
117
|
+
APP_HTTP = 'application/http'
|
118
118
|
def new_content
|
119
119
|
@target =
|
120
120
|
if @target_ct.start_with?(MPS) &&
|
data/lib/safrano/rack_app.rb
CHANGED
@@ -56,7 +56,7 @@ module Safrano
|
|
56
56
|
NOCACHE_HDRS = { 'Cache-Control' => 'no-cache',
|
57
57
|
'Expires' => '-1',
|
58
58
|
'Pragma' => 'no-cache' }.freeze
|
59
|
-
DATASERVICEVERSION = 'DataServiceVersion'
|
59
|
+
DATASERVICEVERSION = 'DataServiceVersion'
|
60
60
|
include MethodHandlers
|
61
61
|
|
62
62
|
def before
|
data/lib/safrano/request.rb
CHANGED
@@ -9,7 +9,7 @@ module Safrano
|
|
9
9
|
# is not passed
|
10
10
|
class Request < Rack::Request
|
11
11
|
HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze
|
12
|
-
HEADER_VAL_RAW = '(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*'
|
12
|
+
HEADER_VAL_RAW = '(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*'
|
13
13
|
HEADER_VAL_WITH_PAR = /(?:#{HEADER_VAL_RAW})\s*(?:;#{HEADER_PARAM})*/.freeze
|
14
14
|
ON_CGST_ERROR = (proc { |r| raise(Sequel::Rollback) if r.in_changeset })
|
15
15
|
|
@@ -194,7 +194,7 @@ module Safrano
|
|
194
194
|
if (rqv = env['HTTP_DATASERVICEVERSION'])
|
195
195
|
if (m = DATASERVICEVERSION_RGX.match(rqv))
|
196
196
|
# client request an too new version --> 501
|
197
|
-
if ((v = m[1]) > Safrano::MAX_DATASERVICE_VERSION)
|
197
|
+
if ((v = m[1]) > Safrano::MAX_DATASERVICE_VERSION) ||
|
198
198
|
(v < Safrano::MIN_DATASERVICE_VERSION)
|
199
199
|
Safrano::VersionNotImplementedError
|
200
200
|
else
|
data/lib/safrano/service.rb
CHANGED
@@ -15,8 +15,8 @@ module Safrano
|
|
15
15
|
# and will be included in Service class
|
16
16
|
module ExpandHandler
|
17
17
|
PATH_SPLITTER = %r{\A(\w+)/?(.*)\z}.freeze
|
18
|
-
DEFERRED = '__deferred'
|
19
|
-
URI = 'uri'
|
18
|
+
DEFERRED = '__deferred'
|
19
|
+
URI = 'uri'
|
20
20
|
|
21
21
|
def get_deferred_odata_h(entity_uri:, attrib:)
|
22
22
|
{ DEFERRED => { URI => "#{entity_uri}/#{attrib}" } }
|
@@ -24,8 +24,8 @@ module Safrano
|
|
24
24
|
|
25
25
|
# default v2
|
26
26
|
# overriden in ServiceV1
|
27
|
-
RESULTS_K = 'results'
|
28
|
-
COUNT_K = '__count'
|
27
|
+
RESULTS_K = 'results'
|
28
|
+
COUNT_K = '__count'
|
29
29
|
def get_coll_odata_h(array:, template:, icount: nil)
|
30
30
|
array.map! do |w|
|
31
31
|
get_entity_odata_h(entity: w, template: template)
|
@@ -40,7 +40,7 @@ module Safrano
|
|
40
40
|
end
|
41
41
|
|
42
42
|
EMPTYH = {}.freeze
|
43
|
-
METADATA_K = '__metadata'
|
43
|
+
METADATA_K = '__metadata'
|
44
44
|
def get_entity_odata_h(entity:, template:)
|
45
45
|
# start with metadata
|
46
46
|
hres = { METADATA_K => entity.metadata_h }
|
@@ -90,21 +90,20 @@ module Safrano
|
|
90
90
|
# xml namespace constants needed for the output of XML service and
|
91
91
|
# and metadata
|
92
92
|
module XMLNS
|
93
|
-
MSFT_ADO = 'http://schemas.microsoft.com/ado'
|
94
|
-
MSFT_ADO_2009_EDM = "#{MSFT_ADO}/2009/11/edm"
|
95
|
-
MSFT_ADO_2007_EDMX = "#{MSFT_ADO}/2007/06/edmx"
|
96
|
-
MSFT_ADO_2007_META = MSFT_ADO
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
W3_2007_APP = 'http://www.w3.org/2007/app'.freeze
|
93
|
+
MSFT_ADO = 'http://schemas.microsoft.com/ado'
|
94
|
+
MSFT_ADO_2009_EDM = "#{MSFT_ADO}/2009/11/edm"
|
95
|
+
MSFT_ADO_2007_EDMX = "#{MSFT_ADO}/2007/06/edmx"
|
96
|
+
MSFT_ADO_2007_META = "#{MSFT_ADO}/2007/08/dataservices/metadata"
|
97
|
+
|
98
|
+
W3_2005_ATOM = 'http://www.w3.org/2005/Atom'
|
99
|
+
W3_2007_APP = 'http://www.w3.org/2007/app'
|
101
100
|
end
|
102
101
|
end
|
103
102
|
|
104
103
|
# Link to Model
|
105
104
|
module Safrano
|
106
|
-
MAX_DATASERVICE_VERSION = '2'
|
107
|
-
MIN_DATASERVICE_VERSION = '1'
|
105
|
+
MAX_DATASERVICE_VERSION = '2'
|
106
|
+
MIN_DATASERVICE_VERSION = '1'
|
108
107
|
CV_MAX_DATASERVICE_VERSION = Contract.valid(MAX_DATASERVICE_VERSION).freeze
|
109
108
|
CV_MIN_DATASERVICE_VERSION = Contract.valid(MIN_DATASERVICE_VERSION).freeze
|
110
109
|
include XMLNS
|
@@ -113,7 +112,7 @@ module Safrano
|
|
113
112
|
include Safrano
|
114
113
|
include ExpandHandler
|
115
114
|
|
116
|
-
XML_PREAMBLE = %Q(<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r\n)
|
115
|
+
XML_PREAMBLE = %Q(<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r\n)
|
117
116
|
|
118
117
|
# This is just a hash of entity Set Names to the corresponding Class
|
119
118
|
# Example
|
@@ -161,8 +160,8 @@ module Safrano
|
|
161
160
|
end
|
162
161
|
|
163
162
|
TRAILING_SLASH = %r{/\z}.freeze
|
164
|
-
DEFAULT_PATH_PREFIX = '/'
|
165
|
-
DEFAULT_SERVER_URL = 'http://localhost:9494'
|
163
|
+
DEFAULT_PATH_PREFIX = '/'
|
164
|
+
DEFAULT_SERVER_URL = 'http://localhost:9494'
|
166
165
|
|
167
166
|
def enable_batch
|
168
167
|
@batch_handler = Safrano::Batch::EnabledHandler.new
|
@@ -530,7 +529,7 @@ module Safrano
|
|
530
529
|
|
531
530
|
# methods related to transitions to next state (cf. walker)
|
532
531
|
module Transitions
|
533
|
-
DOLLAR_ID_REGEXP = Regexp.new('\A\/\$')
|
532
|
+
DOLLAR_ID_REGEXP = Regexp.new('\A\/\$')
|
534
533
|
ALLOWED_TRANSITIONS_FIXED = [
|
535
534
|
Safrano::TransitionEnd,
|
536
535
|
Safrano::TransitionMetadata,
|
data/lib/safrano/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safrano
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- oz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: rfc2047
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
61
|
+
version: '0.3'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
68
|
+
version: '0.3'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -189,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
189
|
- !ruby/object:Gem::Version
|
190
190
|
version: '0'
|
191
191
|
requirements: []
|
192
|
-
rubygems_version: 3.
|
192
|
+
rubygems_version: 3.2.0.rc.2
|
193
193
|
signing_key:
|
194
194
|
specification_version: 4
|
195
195
|
summary: Safrano is a Ruby OData server library based on Sequel and Rack
|