safrano 0.4.4 → 0.4.5
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 +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
|