safrano 0.5.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/core_ext/Date/format.rb +47 -0
- data/lib/core_ext/DateTime/format.rb +54 -0
- data/lib/core_ext/Dir/iter.rb +7 -5
- data/lib/core_ext/Hash/transform.rb +14 -6
- data/lib/core_ext/Numeric/convert.rb +1 -1
- data/lib/core_ext/Time/format.rb +71 -0
- data/lib/core_ext/date.rb +5 -0
- data/lib/core_ext/datetime.rb +5 -0
- data/lib/core_ext/time.rb +5 -0
- data/lib/odata/attribute.rb +8 -6
- data/lib/odata/batch.rb +3 -3
- data/lib/odata/collection.rb +5 -5
- data/lib/odata/collection_filter.rb +3 -1
- data/lib/odata/collection_media.rb +4 -27
- data/lib/odata/collection_order.rb +1 -1
- data/lib/odata/common_logger.rb +5 -27
- data/lib/odata/complex_type.rb +19 -21
- data/lib/odata/edm/primitive_types.rb +14 -19
- data/lib/odata/entity.rb +12 -12
- data/lib/odata/error.rb +7 -7
- data/lib/odata/expand.rb +2 -2
- data/lib/odata/filter/base.rb +10 -1
- data/lib/odata/filter/error.rb +2 -2
- data/lib/odata/filter/parse.rb +16 -2
- data/lib/odata/filter/sequel.rb +31 -4
- data/lib/odata/filter/sequel_datetime_adapter.rb +21 -0
- data/lib/odata/filter/token.rb +18 -5
- data/lib/odata/filter/tree.rb +83 -9
- data/lib/odata/function_import.rb +11 -9
- data/lib/odata/model_ext.rb +26 -29
- data/lib/odata/request/json.rb +171 -0
- data/lib/odata/transition.rb +2 -2
- data/lib/odata/url_parameters.rb +3 -3
- data/lib/odata/walker.rb +1 -1
- data/lib/safrano/multipart.rb +1 -3
- data/lib/safrano/rack_app.rb +2 -14
- data/lib/safrano/rack_builder.rb +0 -15
- data/lib/safrano/request.rb +3 -3
- data/lib/safrano/response.rb +3 -3
- data/lib/safrano/service.rb +13 -5
- data/lib/safrano/type_mapping.rb +4 -4
- data/lib/safrano/version.rb +1 -2
- data/lib/safrano.rb +3 -0
- metadata +51 -15
data/lib/odata/entity.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'json'
|
4
4
|
require 'rexml/document'
|
5
|
-
require 'safrano
|
6
|
-
require 'odata/model_ext
|
5
|
+
require 'safrano'
|
6
|
+
require 'odata/model_ext' # required for self.class.entity_type_name ??
|
7
7
|
require_relative 'navigation_attribute'
|
8
8
|
|
9
9
|
module Safrano
|
@@ -86,9 +86,9 @@ module Safrano
|
|
86
86
|
"#{self.class.uri}#{@odata_pk}"
|
87
87
|
end
|
88
88
|
|
89
|
-
D = 'd'
|
90
|
-
DJ_OPEN = '{"d":'
|
91
|
-
DJ_CLOSE = '}'
|
89
|
+
D = 'd'
|
90
|
+
DJ_OPEN = '{"d":'
|
91
|
+
DJ_CLOSE = '}'
|
92
92
|
|
93
93
|
# Json formatter for a single entity (probably OData V1/V2 like)
|
94
94
|
def to_odata_json(request:)
|
@@ -176,7 +176,7 @@ module Safrano
|
|
176
176
|
destroy(transaction: false)
|
177
177
|
end
|
178
178
|
rescue StandardError => e
|
179
|
-
raise SequelAdapterError
|
179
|
+
raise SequelAdapterError, e
|
180
180
|
end
|
181
181
|
|
182
182
|
# TODO: differentiate between POST/PUT/PATCH/MERGE
|
@@ -203,7 +203,7 @@ module Safrano
|
|
203
203
|
if req.walker.media_value
|
204
204
|
odata_media_value_put(req)
|
205
205
|
elsif req.accept?(APPJSON)
|
206
|
-
data = JSON.
|
206
|
+
data = Safrano::OData::JSON.parse_one(req.body.read, self.class)
|
207
207
|
data.delete('__metadata')
|
208
208
|
|
209
209
|
if req.in_changeset
|
@@ -220,7 +220,7 @@ module Safrano
|
|
220
220
|
end
|
221
221
|
|
222
222
|
def odata_patch(req)
|
223
|
-
req.with_parsed_data do |data|
|
223
|
+
req.with_parsed_data(self.class) do |data|
|
224
224
|
data.delete('__metadata')
|
225
225
|
|
226
226
|
# validate payload column names
|
@@ -401,9 +401,9 @@ module Safrano
|
|
401
401
|
include Entity
|
402
402
|
def pk_uri
|
403
403
|
pku = +''
|
404
|
-
self.class.odata_upk_parts.each_with_index
|
404
|
+
self.class.odata_upk_parts.each_with_index do |upart, i|
|
405
405
|
pku = "#{pku}#{upart}#{pk[i]}"
|
406
|
-
|
406
|
+
end
|
407
407
|
pku
|
408
408
|
end
|
409
409
|
|
@@ -419,7 +419,7 @@ module Safrano
|
|
419
419
|
module EntityCreateStandardOutput
|
420
420
|
# Json formatter for a create entity POST call / Standard version; return as json object
|
421
421
|
def to_odata_create_json(request:)
|
422
|
-
# TODO Perf: reduce method call overhead
|
422
|
+
# TODO: Perf: reduce method call overhead
|
423
423
|
# we added this redirection for readability and flexibility
|
424
424
|
to_odata_json(request: request)
|
425
425
|
end
|
@@ -428,7 +428,7 @@ module Safrano
|
|
428
428
|
module EntityCreateArrayOutput
|
429
429
|
# Json formatter for a create entity POST call Array version
|
430
430
|
def to_odata_create_json(request:)
|
431
|
-
# TODO Perf: reduce method call overhead
|
431
|
+
# TODO: Perf: reduce method call overhead
|
432
432
|
# we added this redirection for readability and flexibility
|
433
433
|
to_odata_array_json(request: request)
|
434
434
|
end
|
data/lib/odata/error.rb
CHANGED
@@ -59,12 +59,12 @@ module Safrano
|
|
59
59
|
message = (m = @msg.to_s).empty? ? to_s : m
|
60
60
|
if req.accept?(APPJSON)
|
61
61
|
# json is default content type so we dont need to specify it here again
|
62
|
-
[
|
63
|
-
{ 'odata.error' => { 'code' =>
|
62
|
+
[http_code, EMPTY_HASH,
|
63
|
+
{ 'odata.error' => { 'code' => http_code.to_s,
|
64
64
|
'type' => to_s,
|
65
65
|
'message' => message } }.to_json]
|
66
66
|
else
|
67
|
-
[
|
67
|
+
[http_code, CT_TEXT, message]
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
@@ -82,8 +82,8 @@ module Safrano
|
|
82
82
|
if req.accept?(APPJSON)
|
83
83
|
# json is default content type so we dont need to specify it here again
|
84
84
|
[self.class.http_code, EMPTY_HASH,
|
85
|
-
{ 'odata.error' => { 'code' =>
|
86
|
-
'type' =>
|
85
|
+
{ 'odata.error' => { 'code' => self.class.http_code.to_s,
|
86
|
+
'type' => self.class.to_s,
|
87
87
|
'message' => message } }.to_json]
|
88
88
|
else
|
89
89
|
[self.class.http_code, CT_TEXT, message]
|
@@ -184,7 +184,7 @@ module Safrano
|
|
184
184
|
class BadRequestSelectInvalidProps < BadRequestError
|
185
185
|
include ErrorInstance
|
186
186
|
def initialize(model, iprops)
|
187
|
-
@msg = (
|
187
|
+
@msg = (iprops.size > 1 ? "Bad Request: the $select properties #{iprops.to_a.join(', ')} are invalid for entityset #{model.entity_set_name}" : "Bad Request: the $select property #{iprops.first} is invalid for entityset #{model.entity_set_name}")
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
@@ -232,7 +232,7 @@ module Safrano
|
|
232
232
|
@msg = 'The requested OData version is not yet supported'
|
233
233
|
end
|
234
234
|
# batch not implemented (Safrano specific)
|
235
|
-
class BatchNotImplementedError <
|
235
|
+
class BatchNotImplementedError < RuntimeError
|
236
236
|
@msg = 'Not implemented: OData batch'
|
237
237
|
end
|
238
238
|
|
data/lib/odata/expand.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'odata/error
|
3
|
+
require 'odata/error'
|
4
4
|
|
5
5
|
# all dataset expanding related classes in our OData module
|
6
6
|
# ie do eager loading
|
@@ -52,7 +52,7 @@ module Safrano
|
|
52
52
|
# [1] --> { 1 => {} }
|
53
53
|
DEEPH_1 = ->(inp) { inp.size > 1 ? { inp[0] => DEEPH_1.call(inp[1..-1]) } : { inp[0] => {} } }
|
54
54
|
|
55
|
-
NODESEP = '/'
|
55
|
+
NODESEP = '/'
|
56
56
|
|
57
57
|
def initialize(exstr)
|
58
58
|
exstr.strip!
|
data/lib/odata/filter/base.rb
CHANGED
@@ -49,10 +49,13 @@ module Safrano
|
|
49
49
|
class ArgTree < Tree
|
50
50
|
end
|
51
51
|
|
52
|
-
# Numbers (floating point, ints
|
52
|
+
# Numbers (floating point, ints)
|
53
53
|
class FPNumber < Leave
|
54
54
|
end
|
55
55
|
|
56
|
+
class DecimalLit < Leave
|
57
|
+
end
|
58
|
+
|
56
59
|
# Literals are unquoted words without /
|
57
60
|
class Literal < Leave
|
58
61
|
end
|
@@ -70,5 +73,11 @@ module Safrano
|
|
70
73
|
# Quoted Strings
|
71
74
|
class QString < Leave
|
72
75
|
end
|
76
|
+
|
77
|
+
# DateTime Literals
|
78
|
+
class DateTimeLit < Leave
|
79
|
+
end
|
80
|
+
class DateTimeOffsetLit < Leave
|
81
|
+
end
|
73
82
|
end
|
74
83
|
end
|
data/lib/odata/filter/error.rb
CHANGED
@@ -16,7 +16,7 @@ module Safrano
|
|
16
16
|
# Parser errors
|
17
17
|
|
18
18
|
class Error
|
19
|
-
def
|
19
|
+
def self.http_code
|
20
20
|
const_get(:HTTP_CODE)
|
21
21
|
end
|
22
22
|
HTTP_CODE = 400
|
@@ -70,7 +70,7 @@ module Safrano
|
|
70
70
|
include ::Safrano::ErrorInstance
|
71
71
|
def initialize(tok, typ, cur)
|
72
72
|
super
|
73
|
-
@msg = "Bad Request: wrong number of parameters for function #{cur.parent.value
|
73
|
+
@msg = "Bad Request: wrong number of parameters for function #{cur.parent.value} in $filter"
|
74
74
|
end
|
75
75
|
end
|
76
76
|
# Invalid separator in this context (missing parenthesis?)
|
data/lib/odata/filter/parse.rb
CHANGED
@@ -174,7 +174,21 @@ module Safrano
|
|
174
174
|
@cursor.update_state(tok, typ)
|
175
175
|
grow_at_cursor(FPNumber.new(tok))
|
176
176
|
end
|
177
|
-
|
177
|
+
when :DecimalLit
|
178
|
+
with_accepted(tok, typ) do
|
179
|
+
@cursor.update_state(tok, typ)
|
180
|
+
grow_at_cursor(DecimalLit.new(tok))
|
181
|
+
end
|
182
|
+
when :DateTimeLit
|
183
|
+
with_accepted(tok, typ) do
|
184
|
+
@cursor.update_state(tok, typ)
|
185
|
+
grow_at_cursor(DateTimeLit.new(tok))
|
186
|
+
end
|
187
|
+
when :DateTimeOffsetLit
|
188
|
+
with_accepted(tok, typ) do
|
189
|
+
@cursor.update_state(tok, typ)
|
190
|
+
grow_at_cursor(DateTimeOffsetLit.new(tok))
|
191
|
+
end
|
178
192
|
when :unmatchedQuote
|
179
193
|
break unmatched_quote_error(tok, typ)
|
180
194
|
|
@@ -188,7 +202,7 @@ module Safrano
|
|
188
202
|
break(@error) if @error
|
189
203
|
end
|
190
204
|
(@error = @tree.check_types) unless @error
|
191
|
-
@error
|
205
|
+
@error || Contract.valid(@tree)
|
192
206
|
end
|
193
207
|
end
|
194
208
|
end
|
data/lib/odata/filter/sequel.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative './base'
|
4
4
|
require_relative './sequel_function_adapter'
|
5
|
-
|
5
|
+
require_relative './sequel_datetime_adapter'
|
6
6
|
module Safrano
|
7
7
|
module Filter
|
8
8
|
# Base class for Leaves, Trees, RootTrees etc
|
@@ -184,9 +184,10 @@ module Safrano
|
|
184
184
|
Contract.collect_result!(@children[0].leuqes(jh),
|
185
185
|
@children[1].leuqes(jh)) do |c0, c1|
|
186
186
|
if c1 == NullLiteral::LEUQES
|
187
|
-
|
187
|
+
case @value
|
188
|
+
when :eq
|
188
189
|
leuqes_op = :IS
|
189
|
-
|
190
|
+
when :ne
|
190
191
|
leuqes_op = :'IS NOT'
|
191
192
|
end
|
192
193
|
end
|
@@ -242,6 +243,12 @@ module Safrano
|
|
242
243
|
end
|
243
244
|
end
|
244
245
|
|
246
|
+
class DecimalLit
|
247
|
+
def leuqes(_jh)
|
248
|
+
success Sequel.lit(@value)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
245
252
|
# Literals are unquoted words
|
246
253
|
class Literal
|
247
254
|
def leuqes(jh)
|
@@ -271,7 +278,7 @@ module Safrano
|
|
271
278
|
|
272
279
|
# Null
|
273
280
|
class NullLiteral
|
274
|
-
def leuqes(
|
281
|
+
def leuqes(_jh)
|
275
282
|
# Sequel's representation of NULL
|
276
283
|
success LEUQES
|
277
284
|
end
|
@@ -304,5 +311,25 @@ module Safrano
|
|
304
311
|
success "%#{@value}%"
|
305
312
|
end
|
306
313
|
end
|
314
|
+
|
315
|
+
# DateTime literals datetime'2017-04-15T00:00:00'
|
316
|
+
class DateTimeLit
|
317
|
+
# datetime method is defined dynamically by adapter-specific include on startup
|
318
|
+
# --> sequel_datetime_adapter.rb
|
319
|
+
def leuqes(_jh)
|
320
|
+
# success Sequel.function(:datetime, @value)
|
321
|
+
success datetime(@value)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# DateTimeOffset literals datetimeoffset'2017-04-15T00:00:00+02:00'
|
326
|
+
class DateTimeOffsetLit
|
327
|
+
# datetime method is defined dynamically by adapter-specific include on startup
|
328
|
+
# --> sequel_datetime_adapter.rb
|
329
|
+
def leuqes(_jh)
|
330
|
+
# success Sequel.function(:datetime, @value)
|
331
|
+
success datetime(@value)
|
332
|
+
end
|
333
|
+
end
|
307
334
|
end
|
308
335
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './tree'
|
4
|
+
require_relative './sequel'
|
5
|
+
|
6
|
+
module Safrano
|
7
|
+
module Filter
|
8
|
+
# sqlite adapter specific DateTime handler
|
9
|
+
module DateTimeSqlite
|
10
|
+
def datetime(_val)
|
11
|
+
Sequel.function(:datetime, @value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
# non-sqlite adapter specific DateTime handler
|
15
|
+
module DateTimeDefault
|
16
|
+
def datetime(_val)
|
17
|
+
Sequel.lit("'#{@value}'")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/odata/filter/token.rb
CHANGED
@@ -15,9 +15,16 @@ module Safrano
|
|
15
15
|
BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'
|
16
16
|
BINOARITHM = '[aA][dD][dD]|[sS][uU][bB]|[mM][uU][lL]|[dD][iI][vV]|[mM][oO][dD]'
|
17
17
|
NOTRGX = 'not|NOT|Not'
|
18
|
-
FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?'
|
18
|
+
FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?[df]?'
|
19
|
+
DECIMALRGX = '\d+(?:\.\d+)[mM]'
|
19
20
|
QUALITRGX = '\w+(?:\/\w+)+'
|
20
|
-
|
21
|
+
# datetime'yyyy-mm-ddThh:mm[:ss[.fffffff]]' NOTE: Spaces are not allowed between datetime and quoted portion.
|
22
|
+
# datetime is case-insensitive
|
23
|
+
DATIRGX = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?:\:\d{2})?(?:\.\d{1,7})?'
|
24
|
+
DATETIMERGX = /datetime'#{DATIRGX}[zZ]?'/i.freeze
|
25
|
+
DATIOFFRGX = /datetimeoffset'#{DATIRGX}(?:[zZ]|[+-]\d{2}:\d{2})'/.freeze
|
26
|
+
|
27
|
+
RGX = /(#{FUNCRGX})|(#{NULLRGX})|([(),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{DECIMALRGX})|(#{FPRGX})|(#{QUALITRGX})|(#{DATETIMERGX})|(#{DATIOFFRGX})|(\w+)|(')/.freeze
|
21
28
|
|
22
29
|
def each_typed_token(inp)
|
23
30
|
typ = nil
|
@@ -52,12 +59,18 @@ module Safrano
|
|
52
59
|
when 6
|
53
60
|
:QString
|
54
61
|
when 7
|
55
|
-
:
|
62
|
+
:DecimalLit
|
56
63
|
when 8
|
57
|
-
:
|
64
|
+
:FPNumber
|
58
65
|
when 9
|
59
|
-
:
|
66
|
+
:Qualit
|
60
67
|
when 10
|
68
|
+
:DateTimeLit
|
69
|
+
when 11
|
70
|
+
:DateTimeOffsetLit
|
71
|
+
when 12
|
72
|
+
:Literal
|
73
|
+
when 13
|
61
74
|
:unmatchedQuote
|
62
75
|
end
|
63
76
|
yield found, typ
|
data/lib/odata/filter/tree.rb
CHANGED
@@ -45,6 +45,11 @@ module Safrano
|
|
45
45
|
super(val, &block)
|
46
46
|
end
|
47
47
|
|
48
|
+
# shortcut used for testing
|
49
|
+
def first_child_value
|
50
|
+
@children.first.value
|
51
|
+
end
|
52
|
+
|
48
53
|
def attach(child)
|
49
54
|
child.parent = self
|
50
55
|
@children << child
|
@@ -66,7 +71,7 @@ module Safrano
|
|
66
71
|
def accept?(tok, typ)
|
67
72
|
case typ
|
68
73
|
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :ArgTree,
|
69
|
-
:UnopTree, :FPNumber
|
74
|
+
:UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit
|
70
75
|
nil
|
71
76
|
when :Delimiter
|
72
77
|
if tok == '('
|
@@ -232,7 +237,8 @@ module Safrano
|
|
232
237
|
|
233
238
|
def update_state(_tok, typ)
|
234
239
|
case typ
|
235
|
-
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm,
|
240
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm,
|
241
|
+
:UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit
|
236
242
|
@state = :closed
|
237
243
|
end
|
238
244
|
end
|
@@ -297,7 +303,8 @@ module Safrano
|
|
297
303
|
@state = :closed
|
298
304
|
when :Separator
|
299
305
|
@state = :sep
|
300
|
-
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
|
306
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber, :DecimalLit,
|
307
|
+
:DateTimeLit, :DateTimeOffsetLit
|
301
308
|
@state = :val
|
302
309
|
end
|
303
310
|
end
|
@@ -307,8 +314,8 @@ module Safrano
|
|
307
314
|
case typ
|
308
315
|
when :Delimiter
|
309
316
|
if @value == '(' && tok == ')' && @state != :closed
|
310
|
-
if (@parent.class == IdentityFuncTree)
|
311
|
-
|
317
|
+
if (@parent.class == IdentityFuncTree) ||
|
318
|
+
@parent.arity_full?(@children.size)
|
312
319
|
|
313
320
|
nil
|
314
321
|
else
|
@@ -327,11 +334,10 @@ module Safrano
|
|
327
334
|
elsif @state == :sep
|
328
335
|
Parser::ErrorInvalidToken.new(tok, typ, self)
|
329
336
|
end
|
330
|
-
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
|
337
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber, :DecimalLit,
|
338
|
+
:DateTimeLit, :DateTimeOffsetLit
|
331
339
|
if (@state == :open) || (@state == :sep)
|
332
|
-
if @parent.arity_full?(@children.size)
|
333
|
-
Parser::ErrorInvalidArity.new(tok, typ, self)
|
334
|
-
end
|
340
|
+
Parser::ErrorInvalidArity.new(tok, typ, self) if @parent.arity_full?(@children.size)
|
335
341
|
else
|
336
342
|
Parser::ErrorInvalidToken.new(tok, typ, self)
|
337
343
|
end
|
@@ -349,6 +355,13 @@ module Safrano
|
|
349
355
|
|
350
356
|
# Numbers (floating point, ints, dec)
|
351
357
|
class FPNumber
|
358
|
+
def initialize(val)
|
359
|
+
# 1.53f --> value 1.53
|
360
|
+
# 1.53d --> value 1.53
|
361
|
+
# 1.53 --> value 1.53
|
362
|
+
val[-1] =~ /[fd]/i ? super(val[0..-2]) : super(val)
|
363
|
+
end
|
364
|
+
|
352
365
|
def accept?(tok, typ)
|
353
366
|
case typ
|
354
367
|
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
@@ -363,7 +376,28 @@ module Safrano
|
|
363
376
|
:number
|
364
377
|
end
|
365
378
|
end
|
379
|
+
class DecimalLit
|
380
|
+
def initialize(val)
|
381
|
+
# 1.53m --> value 1.53
|
382
|
+
# Warning, this assumes that the m|M part in the input is really not optional
|
383
|
+
# cf. DECIMALRGX in token.rb
|
384
|
+
|
385
|
+
super(val[0..-2])
|
386
|
+
end
|
387
|
+
|
388
|
+
def accept?(tok, typ)
|
389
|
+
case typ
|
390
|
+
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
391
|
+
nil
|
392
|
+
else
|
393
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
394
|
+
end
|
395
|
+
end
|
366
396
|
|
397
|
+
def edm_type
|
398
|
+
:decimal
|
399
|
+
end
|
400
|
+
end
|
367
401
|
# Literals are unquoted words without /
|
368
402
|
class Literal
|
369
403
|
def accept?(tok, typ)
|
@@ -409,6 +443,46 @@ module Safrano
|
|
409
443
|
end
|
410
444
|
end
|
411
445
|
|
446
|
+
# DateTimeLit
|
447
|
+
class DateTimeLit
|
448
|
+
def initialize(val)
|
449
|
+
# datetime'2000-12-12T12:00:53' --> value 2000-12-12T12:00:53
|
450
|
+
super(val[9..-2])
|
451
|
+
end
|
452
|
+
|
453
|
+
def accept?(tok, typ)
|
454
|
+
case typ
|
455
|
+
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
456
|
+
nil
|
457
|
+
else
|
458
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
def edm_type
|
463
|
+
:datetime
|
464
|
+
end
|
465
|
+
end
|
466
|
+
# DateTimeOffsetLit
|
467
|
+
class DateTimeOffsetLit
|
468
|
+
def initialize(val)
|
469
|
+
# datetimeoffset'2000-12-12T12:00:53+02:00' --> value 2000-12-12T12:00:53+02:00
|
470
|
+
super(val[15..-2])
|
471
|
+
end
|
472
|
+
|
473
|
+
def accept?(tok, typ)
|
474
|
+
case typ
|
475
|
+
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
476
|
+
nil
|
477
|
+
else
|
478
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
def edm_type
|
483
|
+
:datetimeoffset
|
484
|
+
end
|
485
|
+
end
|
412
486
|
# Quoted Strings
|
413
487
|
class QString
|
414
488
|
DBL_QO = "''"
|
@@ -84,14 +84,14 @@ module Safrano
|
|
84
84
|
def check_missing_params
|
85
85
|
# do we have all parameters provided ? use Set difference to check
|
86
86
|
pkeys = @params.keys.map(&:to_sym).to_set
|
87
|
-
|
87
|
+
if (idiff = @input.keys.to_set - pkeys).empty?
|
88
|
+
Contract::OK
|
89
|
+
else
|
88
90
|
|
89
91
|
Safrano::ServiceOperationParameterMissing.new(
|
90
92
|
missing: idiff.to_a,
|
91
93
|
sopname: @name
|
92
94
|
)
|
93
|
-
else
|
94
|
-
Contract::OK
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -134,12 +134,14 @@ module Safrano
|
|
134
134
|
# EntitySet= @entity_set ,
|
135
135
|
'ReturnType' => @returning.type_metadata,
|
136
136
|
'm:HttpMethod' => @http_method)
|
137
|
-
@input
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
137
|
+
if @input
|
138
|
+
@input.each do |iname, type|
|
139
|
+
funky.add_element('Parameter',
|
140
|
+
'Name' => iname.to_s,
|
141
|
+
'Type' => type.type_name,
|
142
|
+
'Mode' => 'In')
|
143
|
+
end
|
144
|
+
end
|
143
145
|
funky
|
144
146
|
end
|
145
147
|
|