haveapi 0.28.3 → 0.28.4
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/haveapi.gemspec +1 -1
- data/lib/haveapi/extensions/exception_mailer.rb +136 -17
- data/lib/haveapi/server.rb +139 -86
- data/lib/haveapi/version.rb +1 -1
- data/spec/extensions/exception_mailer_spec.rb +195 -0
- data/spec/server/integration_spec.rb +34 -0
- metadata +4 -7
- data/doc/create-client.md +0 -107
- data/doc/json-schema.html +0 -1182
- data/doc/protocol.md +0 -535
- data/doc/protocol.png +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 12a166f4b2cfae2e1046011ec072c2d3cfe2bae2c6e22aa7ccbb8a3a772fc4cd
|
|
4
|
+
data.tar.gz: 919fcc67646a1c52b1410500a74e27b3628403e97f97c095523d78bef4046210
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f818bba5c50921fa6dc188345aabed20988fa499af2de594ad3f44af0614bd0ffe94aec7a13558bc72492f46b2fb1ce6c92b24d7a53af5be964e056d0a1a2fa
|
|
7
|
+
data.tar.gz: 38095fce8f5b1abfa3d630f5188bbed4d1203ea5ce70ec2f8a2f59faab16c1edfb863441a77a87cd1a5155e623331e32a45e434f163d3ed03cb4c3ac3cb77a79
|
data/haveapi.gemspec
CHANGED
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
s.required_ruby_version = ">= #{File.read('../../.ruby-version').strip}"
|
|
16
16
|
|
|
17
17
|
s.add_dependency 'activesupport', '>= 7.1'
|
|
18
|
-
s.add_dependency 'haveapi-client', '~> 0.28.
|
|
18
|
+
s.add_dependency 'haveapi-client', '~> 0.28.4'
|
|
19
19
|
s.add_dependency 'json'
|
|
20
20
|
s.add_dependency 'mail'
|
|
21
21
|
s.add_dependency 'nesty', '~> 1.0'
|
|
@@ -11,6 +11,21 @@ module HaveAPI::Extensions
|
|
|
11
11
|
# is added. Some helper methods are taken either from Sinatra or Rack.
|
|
12
12
|
class ExceptionMailer < Base
|
|
13
13
|
Frame = Struct.new(:filename, :lineno, :function, :context_line)
|
|
14
|
+
FILTERED_VALUE = '[FILTERED]'.freeze
|
|
15
|
+
SENSITIVE_KEY_PATTERN = /
|
|
16
|
+
authorization|cookie|password|passwd|passphrase|secret|token|
|
|
17
|
+
api[_-]?key|credential|jwt|session|csrf|query_string|form_vars|
|
|
18
|
+
request_uri|original_fullpath|fullpath
|
|
19
|
+
/ix
|
|
20
|
+
SENSITIVE_STRING_PATTERN = /
|
|
21
|
+
(
|
|
22
|
+
(?:authorization|cookie|password|passwd|passphrase|secret|token|
|
|
23
|
+
api[_-]?key|credential|jwt|session|csrf)
|
|
24
|
+
[^=:\s&;<>]{0,64}
|
|
25
|
+
\s*(?:=|:|=>)\s*["']?
|
|
26
|
+
)
|
|
27
|
+
[^&;\s<"'}]+
|
|
28
|
+
/ix
|
|
14
29
|
|
|
15
30
|
# @param opts [Hash] options
|
|
16
31
|
# @option opts to [String] recipient address
|
|
@@ -24,21 +39,33 @@ module HaveAPI::Extensions
|
|
|
24
39
|
|
|
25
40
|
def enabled(server)
|
|
26
41
|
HaveAPI::Action.connect_hook(:exec_exception) do |ret, context, e|
|
|
27
|
-
|
|
42
|
+
safe_log(context, e)
|
|
28
43
|
ret
|
|
29
44
|
end
|
|
30
45
|
|
|
31
46
|
server.connect_hook(:description_exception) do |ret, context, e|
|
|
32
|
-
|
|
47
|
+
safe_log(context, e)
|
|
33
48
|
ret
|
|
34
49
|
end
|
|
50
|
+
|
|
51
|
+
server.connect_hook(:request_exception) do |ret, context, e|
|
|
52
|
+
safe_log(context, e)
|
|
53
|
+
ret
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def safe_log(context, exception)
|
|
58
|
+
log(context, exception)
|
|
59
|
+
rescue StandardError => e
|
|
60
|
+
warn "HaveAPI::Extensions::ExceptionMailer failed: #{e.class}: #{e.message}"
|
|
35
61
|
end
|
|
36
62
|
|
|
37
63
|
def log(context, exception)
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
request_context = context&.request
|
|
65
|
+
req = request_context.respond_to?(:request) ? request_context.request : request_context
|
|
66
|
+
path = request_path(context, req)
|
|
40
67
|
|
|
41
|
-
frames = exception.backtrace.map do |line|
|
|
68
|
+
frames = Array(exception.backtrace).map do |line|
|
|
42
69
|
frame = Frame.new
|
|
43
70
|
|
|
44
71
|
next unless line =~ /(.*?):(\d+)(:in `(.*)')?/
|
|
@@ -57,14 +84,21 @@ module HaveAPI::Extensions
|
|
|
57
84
|
|
|
58
85
|
frame
|
|
59
86
|
end.compact
|
|
87
|
+
frames = [Frame.new('(unknown)', 0, nil, nil)] if frames.empty?
|
|
60
88
|
|
|
61
|
-
|
|
89
|
+
args = redact(context&.args)
|
|
90
|
+
path_params = redact(context&.path_params)
|
|
91
|
+
input = redact(context&.input)
|
|
92
|
+
get = request_params(req, :GET)
|
|
93
|
+
post = request_params(req, :POST)
|
|
94
|
+
cookies = request_cookies(req)
|
|
95
|
+
env = redact(request_env(request_context, req))
|
|
62
96
|
|
|
63
97
|
user =
|
|
64
|
-
if context
|
|
98
|
+
if context&.current_user.respond_to?(:id)
|
|
65
99
|
context.current_user.id
|
|
66
100
|
else
|
|
67
|
-
context
|
|
101
|
+
context&.current_user
|
|
68
102
|
end
|
|
69
103
|
|
|
70
104
|
mail(context, exception, TEMPLATE.result(binding))
|
|
@@ -114,6 +148,91 @@ module HaveAPI::Extensions
|
|
|
114
148
|
end
|
|
115
149
|
end
|
|
116
150
|
|
|
151
|
+
def request_path(context, req)
|
|
152
|
+
if req.respond_to?(:script_name) && req.respond_to?(:path_info)
|
|
153
|
+
return filter_query_string((req.script_name + req.path_info).squeeze('/'))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
filter_query_string(
|
|
157
|
+
if context.respond_to?(:resolved_path)
|
|
158
|
+
context.resolved_path
|
|
159
|
+
elsif context.respond_to?(:path)
|
|
160
|
+
context.path
|
|
161
|
+
else
|
|
162
|
+
'(unknown)'
|
|
163
|
+
end
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def request_env(request_context, req)
|
|
168
|
+
if request_context.respond_to?(:env)
|
|
169
|
+
request_context.env
|
|
170
|
+
elsif req.respond_to?(:env)
|
|
171
|
+
req.env
|
|
172
|
+
else
|
|
173
|
+
{}
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def request_params(req, method)
|
|
178
|
+
return {} unless req.respond_to?(method)
|
|
179
|
+
|
|
180
|
+
redact(req.public_send(method) || {})
|
|
181
|
+
rescue StandardError
|
|
182
|
+
{}
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def request_cookies(req)
|
|
186
|
+
return {} unless req.respond_to?(:cookies)
|
|
187
|
+
|
|
188
|
+
cookies = req.cookies || {}
|
|
189
|
+
|
|
190
|
+
cookies.each_with_object({}) do |(key, _value), ret|
|
|
191
|
+
ret[key] = FILTERED_VALUE
|
|
192
|
+
end
|
|
193
|
+
rescue StandardError
|
|
194
|
+
{}
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def filter_query_string(path)
|
|
198
|
+
return path unless path.respond_to?(:sub)
|
|
199
|
+
|
|
200
|
+
path.sub(/\?.*/, "?#{FILTERED_VALUE}")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def redact(value, key = nil, seen = nil)
|
|
204
|
+
return FILTERED_VALUE if sensitive_key?(key)
|
|
205
|
+
|
|
206
|
+
seen ||= {}.compare_by_identity
|
|
207
|
+
|
|
208
|
+
case value
|
|
209
|
+
when Hash
|
|
210
|
+
return value if seen.has_key?(value)
|
|
211
|
+
|
|
212
|
+
seen[value] = true
|
|
213
|
+
value.each_with_object({}) do |(inner_key, inner_value), ret|
|
|
214
|
+
ret[inner_key] = redact(inner_value, inner_key, seen)
|
|
215
|
+
end
|
|
216
|
+
when Array
|
|
217
|
+
return value if seen.has_key?(value)
|
|
218
|
+
|
|
219
|
+
seen[value] = true
|
|
220
|
+
value.map { |inner_value| redact(inner_value, nil, seen) }
|
|
221
|
+
when String
|
|
222
|
+
redact_string(value)
|
|
223
|
+
else
|
|
224
|
+
value
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def sensitive_key?(key)
|
|
229
|
+
key && key.to_s.match?(SENSITIVE_KEY_PATTERN)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def redact_string(value)
|
|
233
|
+
value.gsub(SENSITIVE_STRING_PATTERN, "\\1#{FILTERED_VALUE}")
|
|
234
|
+
end
|
|
235
|
+
|
|
117
236
|
TEMPLATE = ERB.new(<<~END
|
|
118
237
|
<!DOCTYPE html>
|
|
119
238
|
<html>
|
|
@@ -224,15 +343,15 @@ module HaveAPI::Extensions
|
|
|
224
343
|
</tr>
|
|
225
344
|
<tr>
|
|
226
345
|
<th>Arguments</th>
|
|
227
|
-
<td><%=h
|
|
346
|
+
<td><%=h args %></td>
|
|
228
347
|
</tr>
|
|
229
348
|
<tr>
|
|
230
349
|
<th>Path parameters</th>
|
|
231
|
-
<td><%=h
|
|
350
|
+
<td><%=h path_params %></td>
|
|
232
351
|
</tr>
|
|
233
352
|
<tr>
|
|
234
353
|
<th>Input</th>
|
|
235
|
-
<td><%=h
|
|
354
|
+
<td><%=h input %></td>
|
|
236
355
|
</tr>
|
|
237
356
|
<tr>
|
|
238
357
|
<th>User</th>
|
|
@@ -269,13 +388,13 @@ module HaveAPI::Extensions
|
|
|
269
388
|
</div> <!-- /BACKTRACE -->
|
|
270
389
|
<div id="get">
|
|
271
390
|
<h3 id="get-info">GET</h3>
|
|
272
|
-
<% if
|
|
391
|
+
<% if !get.empty? %>
|
|
273
392
|
<table class="req">
|
|
274
393
|
<tr>
|
|
275
394
|
<th>Variable</th>
|
|
276
395
|
<th>Value</th>
|
|
277
396
|
</tr>
|
|
278
|
-
<%
|
|
397
|
+
<% get.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
|
279
398
|
<tr>
|
|
280
399
|
<td><%=h key %></td>
|
|
281
400
|
<td class="code"><div><%=h val.inspect %></div></td>
|
|
@@ -289,13 +408,13 @@ module HaveAPI::Extensions
|
|
|
289
408
|
</div> <!-- /GET -->
|
|
290
409
|
<div id="post">
|
|
291
410
|
<h3 id="post-info">POST</h3>
|
|
292
|
-
<% if
|
|
411
|
+
<% if !post.empty? %>
|
|
293
412
|
<table class="req">
|
|
294
413
|
<tr>
|
|
295
414
|
<th>Variable</th>
|
|
296
415
|
<th>Value</th>
|
|
297
416
|
</tr>
|
|
298
|
-
<%
|
|
417
|
+
<% post.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
|
299
418
|
<tr>
|
|
300
419
|
<td><%=h key %></td>
|
|
301
420
|
<td class="code"><div><%=h val.inspect %></div></td>
|
|
@@ -309,13 +428,13 @@ module HaveAPI::Extensions
|
|
|
309
428
|
</div> <!-- /POST -->
|
|
310
429
|
<div id="cookies">
|
|
311
430
|
<h3 id="cookie-info">COOKIES</h3>
|
|
312
|
-
<% unless
|
|
431
|
+
<% unless cookies.empty? %>
|
|
313
432
|
<table class="req">
|
|
314
433
|
<tr>
|
|
315
434
|
<th>Variable</th>
|
|
316
435
|
<th>Value</th>
|
|
317
436
|
</tr>
|
|
318
|
-
<%
|
|
437
|
+
<% cookies.each { |key, val| %>
|
|
319
438
|
<tr>
|
|
320
439
|
<td><%=h key %></td>
|
|
321
440
|
<td class="code"><div><%=h val.inspect %></div></td>
|
data/lib/haveapi/server.rb
CHANGED
|
@@ -45,6 +45,17 @@ module HaveAPI
|
|
|
45
45
|
message: 'error message sent to the client'
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
has_hook :request_exception,
|
|
49
|
+
desc: 'Called when an exception occurs outside action execution',
|
|
50
|
+
args: {
|
|
51
|
+
context: 'HaveAPI::Context',
|
|
52
|
+
exception: 'exception instance'
|
|
53
|
+
},
|
|
54
|
+
ret: {
|
|
55
|
+
http_status: 'HTTP status code to send to client',
|
|
56
|
+
message: 'error message sent to the client'
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
module ServerHelpers
|
|
49
60
|
def setup_formatter
|
|
50
61
|
return if @formatter
|
|
@@ -137,6 +148,32 @@ module HaveAPI
|
|
|
137
148
|
halt code, headers, @formatter.format(false, nil, msg, version: false)
|
|
138
149
|
end
|
|
139
150
|
|
|
151
|
+
def report_exception(exception, context = nil)
|
|
152
|
+
context ||= Context.new(
|
|
153
|
+
settings.api_server,
|
|
154
|
+
request: self,
|
|
155
|
+
params:,
|
|
156
|
+
endpoint: true
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
tmp =
|
|
160
|
+
begin
|
|
161
|
+
settings.api_server.call_hooks_for(
|
|
162
|
+
:request_exception,
|
|
163
|
+
args: [context, exception]
|
|
164
|
+
)
|
|
165
|
+
rescue StandardError => e
|
|
166
|
+
warn "HaveAPI request exception hook failed: #{e.class}: #{e.message}"
|
|
167
|
+
{}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
report_error(
|
|
171
|
+
tmp[:http_status] || 500,
|
|
172
|
+
{},
|
|
173
|
+
tmp[:message] || 'Server error occurred'
|
|
174
|
+
)
|
|
175
|
+
end
|
|
176
|
+
|
|
140
177
|
def root
|
|
141
178
|
settings.api_server.root
|
|
142
179
|
end
|
|
@@ -295,6 +332,10 @@ module HaveAPI
|
|
|
295
332
|
report_error(404, {}, 'Action not found') unless @halted
|
|
296
333
|
end
|
|
297
334
|
|
|
335
|
+
error do
|
|
336
|
+
report_exception(env['sinatra.error'])
|
|
337
|
+
end
|
|
338
|
+
|
|
298
339
|
after do
|
|
299
340
|
if Object.const_defined?(:ActiveRecord)
|
|
300
341
|
ActiveRecord::Base.connection_handler.clear_active_connections!
|
|
@@ -528,113 +569,125 @@ module HaveAPI
|
|
|
528
569
|
|
|
529
570
|
def mount_action(v, route)
|
|
530
571
|
@sinatra.method(route.http_method).call(route.sinatra_path) do
|
|
531
|
-
|
|
572
|
+
context = nil
|
|
532
573
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
else
|
|
536
|
-
authenticated?(v)
|
|
537
|
-
end
|
|
574
|
+
begin
|
|
575
|
+
setup_formatter
|
|
538
576
|
|
|
539
|
-
|
|
540
|
-
|
|
577
|
+
if route.action.auth || settings.api_server.action_state_auth_required?(route)
|
|
578
|
+
authenticate!(v)
|
|
579
|
+
else
|
|
580
|
+
authenticated?(v)
|
|
581
|
+
end
|
|
541
582
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
end
|
|
583
|
+
raw_body = request.body ? request.body.read : ''
|
|
584
|
+
body_method = !%i[get head options].include?(route.http_method.to_sym)
|
|
545
585
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
report_error(400, {}, 'Bad JSON syntax')
|
|
550
|
-
end
|
|
586
|
+
if body_method && !raw_body.empty? && !settings.api_server.send(:json_content_type?, request)
|
|
587
|
+
report_error(415, {}, 'Unsupported Content-Type')
|
|
588
|
+
end
|
|
551
589
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
590
|
+
begin
|
|
591
|
+
body = raw_body.empty? ? nil : JSON.parse(raw_body)
|
|
592
|
+
rescue JSON::ParserError
|
|
593
|
+
report_error(400, {}, 'Bad JSON syntax')
|
|
594
|
+
end
|
|
555
595
|
|
|
556
|
-
|
|
557
|
-
|
|
596
|
+
if !raw_body.empty? && !body.is_a?(Hash)
|
|
597
|
+
report_error(400, {}, 'JSON body must be an object')
|
|
598
|
+
end
|
|
558
599
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
600
|
+
action_params = settings.api_server.send(:path_params, route, params)
|
|
601
|
+
action_input = body_method ? (body || {}) : request.GET
|
|
602
|
+
|
|
603
|
+
context = Context.new(
|
|
604
|
+
settings.api_server,
|
|
605
|
+
version: v,
|
|
606
|
+
request: self,
|
|
607
|
+
action: route.action,
|
|
608
|
+
path: route.path,
|
|
609
|
+
path_params: action_params,
|
|
610
|
+
input: action_input,
|
|
611
|
+
user: current_user,
|
|
612
|
+
endpoint: true,
|
|
613
|
+
resource_path: route.resource_path
|
|
614
|
+
)
|
|
571
615
|
|
|
572
|
-
|
|
616
|
+
action = route.action.new(request, v, action_params, action_input, context)
|
|
573
617
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
618
|
+
unless action.authorized?(current_user)
|
|
619
|
+
report_error(403, {}, 'Access denied. Insufficient permissions.')
|
|
620
|
+
end
|
|
577
621
|
|
|
578
|
-
|
|
579
|
-
|
|
622
|
+
status, reply, errors, http_status = action.safe_exec
|
|
623
|
+
@halted = true
|
|
580
624
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
625
|
+
[
|
|
626
|
+
http_status || 200,
|
|
627
|
+
@formatter.format(
|
|
628
|
+
status,
|
|
629
|
+
status ? reply : nil,
|
|
630
|
+
status ? nil : reply,
|
|
631
|
+
errors,
|
|
632
|
+
version: false
|
|
633
|
+
)
|
|
634
|
+
]
|
|
635
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
636
|
+
report_exception(e, context)
|
|
637
|
+
end
|
|
591
638
|
end
|
|
592
639
|
|
|
593
640
|
@sinatra.options route.sinatra_path do |*args|
|
|
594
|
-
|
|
595
|
-
access_control
|
|
596
|
-
route_method = route.http_method.to_s.upcase
|
|
641
|
+
ctx = nil
|
|
597
642
|
|
|
598
|
-
|
|
643
|
+
begin
|
|
644
|
+
setup_formatter
|
|
645
|
+
access_control
|
|
646
|
+
route_method = route.http_method.to_s.upcase
|
|
599
647
|
|
|
600
|
-
|
|
601
|
-
authenticate!(v)
|
|
602
|
-
else
|
|
603
|
-
authenticated?(v)
|
|
604
|
-
end
|
|
648
|
+
pass if params[:method] && params[:method] != route_method
|
|
605
649
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
path: route.path,
|
|
612
|
-
args:,
|
|
613
|
-
params:,
|
|
614
|
-
user: current_user,
|
|
615
|
-
endpoint: true,
|
|
616
|
-
resource_path: route.resource_path,
|
|
617
|
-
doc: true
|
|
618
|
-
)
|
|
650
|
+
if route.action.auth || settings.api_server.action_state_auth_required?(route)
|
|
651
|
+
authenticate!(v)
|
|
652
|
+
else
|
|
653
|
+
authenticated?(v)
|
|
654
|
+
end
|
|
619
655
|
|
|
620
|
-
|
|
621
|
-
|
|
656
|
+
ctx = Context.new(
|
|
657
|
+
settings.api_server,
|
|
658
|
+
version: v,
|
|
659
|
+
request: self,
|
|
660
|
+
action: route.action,
|
|
661
|
+
path: route.path,
|
|
662
|
+
args:,
|
|
663
|
+
params:,
|
|
664
|
+
user: current_user,
|
|
665
|
+
endpoint: true,
|
|
666
|
+
resource_path: route.resource_path,
|
|
667
|
+
doc: true
|
|
668
|
+
)
|
|
622
669
|
|
|
623
|
-
|
|
624
|
-
|
|
670
|
+
begin
|
|
671
|
+
desc = route.action.describe(ctx)
|
|
672
|
+
|
|
673
|
+
unless desc
|
|
674
|
+
report_error(403, {}, 'Access denied. Insufficient permissions.')
|
|
675
|
+
end
|
|
676
|
+
rescue ValidationError => e
|
|
677
|
+
report_error(400, e.to_hash, e.message)
|
|
678
|
+
rescue StandardError => e
|
|
679
|
+
tmp = settings.api_server.call_hooks_for(:description_exception, args: [ctx, e])
|
|
680
|
+
report_error(
|
|
681
|
+
tmp[:http_status] || 500,
|
|
682
|
+
{},
|
|
683
|
+
tmp[:message] || 'Server error occured'
|
|
684
|
+
)
|
|
625
685
|
end
|
|
626
|
-
rescue ValidationError => e
|
|
627
|
-
report_error(400, e.to_hash, e.message)
|
|
628
|
-
rescue StandardError => e
|
|
629
|
-
tmp = settings.api_server.call_hooks_for(:description_exception, args: [ctx, e])
|
|
630
|
-
report_error(
|
|
631
|
-
tmp[:http_status] || 500,
|
|
632
|
-
{},
|
|
633
|
-
tmp[:message] || 'Server error occured'
|
|
634
|
-
)
|
|
635
|
-
end
|
|
636
686
|
|
|
637
|
-
|
|
687
|
+
@formatter.format(true, desc)
|
|
688
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
689
|
+
report_exception(e, ctx)
|
|
690
|
+
end
|
|
638
691
|
end
|
|
639
692
|
end
|
|
640
693
|
|
data/lib/haveapi/version.rb
CHANGED