haveapi 0.28.3 → 0.29.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/README.md +139 -0
- data/Rakefile +1 -0
- data/haveapi.gemspec +2 -1
- data/lib/haveapi/action.rb +26 -9
- data/lib/haveapi/actions/default.rb +5 -2
- data/lib/haveapi/actions/paginable.rb +8 -4
- data/lib/haveapi/authentication/oauth2/provider.rb +1 -1
- data/lib/haveapi/authentication/token/config.rb +10 -3
- data/lib/haveapi/authentication/token/provider.rb +22 -21
- data/lib/haveapi/context.rb +4 -1
- data/lib/haveapi/extensions/exception_mailer.rb +136 -17
- data/lib/haveapi/i18n.rb +125 -0
- data/lib/haveapi/locales/cs.yml +167 -0
- data/lib/haveapi/locales/en.yml +168 -0
- data/lib/haveapi/metadata.rb +25 -3
- data/lib/haveapi/model_adapters/active_record.rb +40 -26
- data/lib/haveapi/output_formatter.rb +2 -2
- data/lib/haveapi/parameters/metadata_i18n.rb +179 -0
- data/lib/haveapi/parameters/resource.rb +18 -7
- data/lib/haveapi/parameters/typed.rb +27 -20
- data/lib/haveapi/params.rb +76 -7
- data/lib/haveapi/resource.rb +1 -1
- data/lib/haveapi/resources/action_state.rb +47 -27
- data/lib/haveapi/server.rb +287 -94
- data/lib/haveapi/spec/api_builder.rb +25 -0
- data/lib/haveapi/spec/spec_methods.rb +10 -0
- data/lib/haveapi/tasks/i18n.rb +198 -0
- data/lib/haveapi/validator_chain.rb +1 -1
- data/lib/haveapi/validators/acceptance.rb +5 -2
- data/lib/haveapi/validators/confirmation.rb +5 -2
- data/lib/haveapi/validators/exclusion.rb +2 -2
- data/lib/haveapi/validators/format.rb +5 -2
- data/lib/haveapi/validators/inclusion.rb +2 -2
- data/lib/haveapi/validators/length.rb +5 -5
- data/lib/haveapi/validators/numericality.rb +20 -22
- data/lib/haveapi/validators/presence.rb +4 -2
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi.rb +1 -0
- data/spec/authentication/oauth2_spec.rb +10 -0
- data/spec/extensions/exception_mailer_spec.rb +195 -0
- data/spec/i18n_spec.rb +520 -0
- data/spec/params_spec.rb +183 -0
- data/spec/server/integration_spec.rb +34 -0
- metadata +30 -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
data/lib/haveapi/server.rb
CHANGED
|
@@ -6,9 +6,10 @@ require 'haveapi/hooks'
|
|
|
6
6
|
|
|
7
7
|
module HaveAPI
|
|
8
8
|
class Server
|
|
9
|
-
attr_accessor :default_version, :action_state, :validation_error_http_status
|
|
9
|
+
attr_accessor :default_version, :action_state, :validation_error_http_status,
|
|
10
|
+
:default_locale, :parameter_i18n_scope
|
|
10
11
|
attr_reader :root, :routes, :module_name, :auth_chain, :versions, :extensions,
|
|
11
|
-
:action_state_auth
|
|
12
|
+
:action_state_auth, :locale_header, :available_locales
|
|
12
13
|
|
|
13
14
|
include Hookable
|
|
14
15
|
|
|
@@ -45,7 +46,62 @@ module HaveAPI
|
|
|
45
46
|
message: 'error message sent to the client'
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
has_hook :request_exception,
|
|
50
|
+
desc: 'Called when an exception occurs outside action execution',
|
|
51
|
+
args: {
|
|
52
|
+
context: 'HaveAPI::Context',
|
|
53
|
+
exception: 'exception instance'
|
|
54
|
+
},
|
|
55
|
+
ret: {
|
|
56
|
+
http_status: 'HTTP status code to send to client',
|
|
57
|
+
message: 'error message sent to the client'
|
|
58
|
+
}
|
|
59
|
+
|
|
48
60
|
module ServerHelpers
|
|
61
|
+
def setup_request_locale
|
|
62
|
+
return if @haveapi_locale_setup
|
|
63
|
+
|
|
64
|
+
server = settings.api_server
|
|
65
|
+
@haveapi_previous_locale = ::I18n.locale
|
|
66
|
+
locale_header_value = server.locale_header_value(request)
|
|
67
|
+
@haveapi_locale_requested = !locale_header_value.nil?
|
|
68
|
+
@haveapi_explicit_locale = server.request_locale(request)
|
|
69
|
+
@haveapi_locale = if @haveapi_locale_requested
|
|
70
|
+
@haveapi_explicit_locale || server.default_locale
|
|
71
|
+
else
|
|
72
|
+
server.resolved_locale(
|
|
73
|
+
request:,
|
|
74
|
+
current_user: nil
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
server.activate_locale(@haveapi_locale)
|
|
78
|
+
add_vary_header(server.locale_header) if server.locale_header
|
|
79
|
+
@haveapi_locale_setup = true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def resolve_request_locale(user = current_user)
|
|
83
|
+
setup_request_locale
|
|
84
|
+
return @haveapi_locale if @haveapi_locale_requested
|
|
85
|
+
|
|
86
|
+
@haveapi_locale = settings.api_server.resolved_locale(
|
|
87
|
+
request:,
|
|
88
|
+
current_user: user
|
|
89
|
+
)
|
|
90
|
+
settings.api_server.activate_locale(@haveapi_locale)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def restore_request_locale
|
|
94
|
+
return unless @haveapi_locale_setup
|
|
95
|
+
|
|
96
|
+
settings.api_server.activate_locale(@haveapi_previous_locale)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def add_vary_header(name)
|
|
100
|
+
values = response['Vary'].to_s.split(/\s*,\s*/).reject(&:empty?)
|
|
101
|
+
values << name unless values.include?(name)
|
|
102
|
+
headers 'Vary' => values.join(', ')
|
|
103
|
+
end
|
|
104
|
+
|
|
49
105
|
def setup_formatter
|
|
50
106
|
return if @formatter
|
|
51
107
|
|
|
@@ -53,7 +109,7 @@ module HaveAPI
|
|
|
53
109
|
accept = request.accept
|
|
54
110
|
rescue ArgumentError, EncodingError
|
|
55
111
|
@formatter.supports?([])
|
|
56
|
-
report_error(400, {}, '
|
|
112
|
+
report_error(400, {}, HaveAPI.message('haveapi.errors.bad_accept_header'))
|
|
57
113
|
else
|
|
58
114
|
unless @formatter.supports?(accept)
|
|
59
115
|
@halted = true
|
|
@@ -68,7 +124,10 @@ module HaveAPI
|
|
|
68
124
|
end
|
|
69
125
|
|
|
70
126
|
def authenticated?(v)
|
|
71
|
-
|
|
127
|
+
if @current_user
|
|
128
|
+
resolve_request_locale
|
|
129
|
+
return @current_user
|
|
130
|
+
end
|
|
72
131
|
|
|
73
132
|
begin
|
|
74
133
|
@current_user = settings.api_server.send(:do_authenticate, v, request)
|
|
@@ -81,12 +140,13 @@ module HaveAPI
|
|
|
81
140
|
report_error(400, {}, e.message)
|
|
82
141
|
end
|
|
83
142
|
settings.api_server.call_hooks_for(:post_authenticated, args: [@current_user])
|
|
143
|
+
resolve_request_locale
|
|
84
144
|
@current_user
|
|
85
145
|
end
|
|
86
146
|
|
|
87
147
|
def authenticated_versions
|
|
88
|
-
settings.api_server.versions.each_with_object({}) do |v,
|
|
89
|
-
|
|
148
|
+
ret = settings.api_server.versions.each_with_object({}) do |v, users|
|
|
149
|
+
users[v] = settings.api_server.send(:do_authenticate, v, request)
|
|
90
150
|
rescue HaveAPI::Authentication::TokenConflict => e
|
|
91
151
|
unless @formatter
|
|
92
152
|
@formatter = OutputFormatter.new
|
|
@@ -95,6 +155,9 @@ module HaveAPI
|
|
|
95
155
|
|
|
96
156
|
report_error(400, {}, e.message)
|
|
97
157
|
end
|
|
158
|
+
|
|
159
|
+
resolve_request_locale(ret[settings.api_server.default_version] || ret.values.compact.first)
|
|
160
|
+
ret
|
|
98
161
|
end
|
|
99
162
|
|
|
100
163
|
def access_control
|
|
@@ -122,7 +185,7 @@ module HaveAPI
|
|
|
122
185
|
report_error(
|
|
123
186
|
401,
|
|
124
187
|
{ 'www-authenticate' => 'Basic realm="Restricted Area"' },
|
|
125
|
-
'
|
|
188
|
+
HaveAPI.message('haveapi.authentication.required')
|
|
126
189
|
)
|
|
127
190
|
end
|
|
128
191
|
|
|
@@ -137,6 +200,32 @@ module HaveAPI
|
|
|
137
200
|
halt code, headers, @formatter.format(false, nil, msg, version: false)
|
|
138
201
|
end
|
|
139
202
|
|
|
203
|
+
def report_exception(exception, context = nil)
|
|
204
|
+
context ||= Context.new(
|
|
205
|
+
settings.api_server,
|
|
206
|
+
request: self,
|
|
207
|
+
params:,
|
|
208
|
+
endpoint: true
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
tmp =
|
|
212
|
+
begin
|
|
213
|
+
settings.api_server.call_hooks_for(
|
|
214
|
+
:request_exception,
|
|
215
|
+
args: [context, exception]
|
|
216
|
+
)
|
|
217
|
+
rescue StandardError => e
|
|
218
|
+
warn "HaveAPI request exception hook failed: #{e.class}: #{e.message}"
|
|
219
|
+
{}
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
report_error(
|
|
223
|
+
tmp[:http_status] || 500,
|
|
224
|
+
{},
|
|
225
|
+
tmp[:message] || HaveAPI.message('haveapi.errors.server_error')
|
|
226
|
+
)
|
|
227
|
+
end
|
|
228
|
+
|
|
140
229
|
def root
|
|
141
230
|
settings.api_server.root
|
|
142
231
|
end
|
|
@@ -223,6 +312,62 @@ module HaveAPI
|
|
|
223
312
|
@extensions = []
|
|
224
313
|
@action_state_auth = :backend
|
|
225
314
|
@validation_error_http_status = nil
|
|
315
|
+
@default_locale = :en
|
|
316
|
+
self.available_locales = HaveAPI::I18n.available_locales
|
|
317
|
+
self.locale_header = 'Accept-Language'
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def available_locales=(locales)
|
|
321
|
+
@available_locales = Array(locales)
|
|
322
|
+
allow_i18n_locales(@available_locales)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def locale_header=(header)
|
|
326
|
+
@locale_header = header
|
|
327
|
+
allow_header(header) if header
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def activate_locale(locale)
|
|
331
|
+
allow_i18n_locales([locale])
|
|
332
|
+
::I18n.locale = locale
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def allow_i18n_locales(locales)
|
|
336
|
+
requested = Array(locales).compact.map { |locale| locale.to_s.to_sym }
|
|
337
|
+
current = ::I18n.available_locales
|
|
338
|
+
missing = requested.reject do |locale|
|
|
339
|
+
current.any? { |available| available.to_s == locale.to_s }
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
::I18n.available_locales = current + missing unless missing.empty?
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def locale(&block)
|
|
346
|
+
@locale_resolver = block if block
|
|
347
|
+
@locale_resolver
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def request_locale(request)
|
|
351
|
+
HaveAPI::I18n.accept_language(
|
|
352
|
+
locale_header_value(request),
|
|
353
|
+
available_locales
|
|
354
|
+
)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def locale_header_value(request)
|
|
358
|
+
request.env["HTTP_#{locale_header.to_s.upcase.tr('-', '_')}"]
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def resolved_locale(request:, current_user:)
|
|
362
|
+
locale = if @locale_resolver
|
|
363
|
+
@locale_resolver.call(
|
|
364
|
+
request:,
|
|
365
|
+
current_user:,
|
|
366
|
+
default_locale:
|
|
367
|
+
)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
HaveAPI::I18n.normalize_locale(locale, available_locales) || default_locale
|
|
226
371
|
end
|
|
227
372
|
|
|
228
373
|
def action_state_auth=(mode)
|
|
@@ -284,6 +429,8 @@ module HaveAPI
|
|
|
284
429
|
helpers DocHelpers
|
|
285
430
|
|
|
286
431
|
before do
|
|
432
|
+
setup_request_locale
|
|
433
|
+
|
|
287
434
|
if request.env['HTTP_ORIGIN']
|
|
288
435
|
headers 'access-control-allow-origin' => '*',
|
|
289
436
|
'access-control-allow-credentials' => 'false'
|
|
@@ -292,10 +439,16 @@ module HaveAPI
|
|
|
292
439
|
|
|
293
440
|
not_found do
|
|
294
441
|
setup_formatter
|
|
295
|
-
report_error(404, {}, '
|
|
442
|
+
report_error(404, {}, HaveAPI.message('haveapi.errors.action_not_found')) unless @halted
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
error do
|
|
446
|
+
report_exception(env['sinatra.error'])
|
|
296
447
|
end
|
|
297
448
|
|
|
298
449
|
after do
|
|
450
|
+
restore_request_locale
|
|
451
|
+
|
|
299
452
|
if Object.const_defined?(:ActiveRecord)
|
|
300
453
|
ActiveRecord::Base.connection_handler.clear_active_connections!
|
|
301
454
|
end
|
|
@@ -493,6 +646,27 @@ module HaveAPI
|
|
|
493
646
|
end
|
|
494
647
|
end
|
|
495
648
|
|
|
649
|
+
def collect_parameter_metadata_i18n_items(resources, context)
|
|
650
|
+
resources.flat_map do |resource, children|
|
|
651
|
+
original_resource_path = context.resource_path
|
|
652
|
+
context.resource_path = context.resource_path + [resource.resource_name.underscore]
|
|
653
|
+
|
|
654
|
+
action_items = children[:actions].flat_map do |action, path|
|
|
655
|
+
context.action = action
|
|
656
|
+
context.path = path
|
|
657
|
+
|
|
658
|
+
action.parameter_metadata_i18n_items(context)
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
child_items = collect_parameter_metadata_i18n_items(children[:resources], context)
|
|
662
|
+
|
|
663
|
+
action_items + child_items
|
|
664
|
+
ensure
|
|
665
|
+
context.resource_path = original_resource_path
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
private :collect_parameter_metadata_i18n_items
|
|
669
|
+
|
|
496
670
|
def mount_resource(prefix, v, resource, hash)
|
|
497
671
|
hash[resource] = { resources: {}, actions: {} }
|
|
498
672
|
|
|
@@ -528,113 +702,125 @@ module HaveAPI
|
|
|
528
702
|
|
|
529
703
|
def mount_action(v, route)
|
|
530
704
|
@sinatra.method(route.http_method).call(route.sinatra_path) do
|
|
531
|
-
|
|
705
|
+
context = nil
|
|
532
706
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
else
|
|
536
|
-
authenticated?(v)
|
|
537
|
-
end
|
|
707
|
+
begin
|
|
708
|
+
setup_formatter
|
|
538
709
|
|
|
539
|
-
|
|
540
|
-
|
|
710
|
+
if route.action.auth || settings.api_server.action_state_auth_required?(route)
|
|
711
|
+
authenticate!(v)
|
|
712
|
+
else
|
|
713
|
+
authenticated?(v)
|
|
714
|
+
end
|
|
541
715
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
end
|
|
716
|
+
raw_body = request.body ? request.body.read : ''
|
|
717
|
+
body_method = !%i[get head options].include?(route.http_method.to_sym)
|
|
545
718
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
report_error(400, {}, 'Bad JSON syntax')
|
|
550
|
-
end
|
|
719
|
+
if body_method && !raw_body.empty? && !settings.api_server.send(:json_content_type?, request)
|
|
720
|
+
report_error(415, {}, HaveAPI.message('haveapi.errors.unsupported_content_type'))
|
|
721
|
+
end
|
|
551
722
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
723
|
+
begin
|
|
724
|
+
body = raw_body.empty? ? nil : JSON.parse(raw_body)
|
|
725
|
+
rescue JSON::ParserError
|
|
726
|
+
report_error(400, {}, HaveAPI.message('haveapi.errors.bad_json_syntax'))
|
|
727
|
+
end
|
|
555
728
|
|
|
556
|
-
|
|
557
|
-
|
|
729
|
+
if !raw_body.empty? && !body.is_a?(Hash)
|
|
730
|
+
report_error(400, {}, HaveAPI.message('haveapi.errors.json_body_object'))
|
|
731
|
+
end
|
|
558
732
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
733
|
+
action_params = settings.api_server.send(:path_params, route, params)
|
|
734
|
+
action_input = body_method ? (body || {}) : request.GET
|
|
735
|
+
|
|
736
|
+
context = Context.new(
|
|
737
|
+
settings.api_server,
|
|
738
|
+
version: v,
|
|
739
|
+
request: self,
|
|
740
|
+
action: route.action,
|
|
741
|
+
path: route.path,
|
|
742
|
+
path_params: action_params,
|
|
743
|
+
input: action_input,
|
|
744
|
+
user: current_user,
|
|
745
|
+
endpoint: true,
|
|
746
|
+
resource_path: route.resource_path
|
|
747
|
+
)
|
|
571
748
|
|
|
572
|
-
|
|
749
|
+
action = route.action.new(request, v, action_params, action_input, context)
|
|
573
750
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
751
|
+
unless action.authorized?(current_user)
|
|
752
|
+
report_error(403, {}, HaveAPI.message('haveapi.authorization.insufficient_permissions'))
|
|
753
|
+
end
|
|
577
754
|
|
|
578
|
-
|
|
579
|
-
|
|
755
|
+
status, reply, errors, http_status = action.safe_exec
|
|
756
|
+
@halted = true
|
|
580
757
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
758
|
+
[
|
|
759
|
+
http_status || 200,
|
|
760
|
+
@formatter.format(
|
|
761
|
+
status,
|
|
762
|
+
status ? reply : nil,
|
|
763
|
+
status ? nil : reply,
|
|
764
|
+
errors,
|
|
765
|
+
version: false
|
|
766
|
+
)
|
|
767
|
+
]
|
|
768
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
769
|
+
report_exception(e, context)
|
|
770
|
+
end
|
|
591
771
|
end
|
|
592
772
|
|
|
593
773
|
@sinatra.options route.sinatra_path do |*args|
|
|
594
|
-
|
|
595
|
-
access_control
|
|
596
|
-
route_method = route.http_method.to_s.upcase
|
|
774
|
+
ctx = nil
|
|
597
775
|
|
|
598
|
-
|
|
776
|
+
begin
|
|
777
|
+
setup_formatter
|
|
778
|
+
access_control
|
|
779
|
+
route_method = route.http_method.to_s.upcase
|
|
599
780
|
|
|
600
|
-
|
|
601
|
-
authenticate!(v)
|
|
602
|
-
else
|
|
603
|
-
authenticated?(v)
|
|
604
|
-
end
|
|
781
|
+
pass if params[:method] && params[:method] != route_method
|
|
605
782
|
|
|
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
|
-
)
|
|
783
|
+
if route.action.auth || settings.api_server.action_state_auth_required?(route)
|
|
784
|
+
authenticate!(v)
|
|
785
|
+
else
|
|
786
|
+
authenticated?(v)
|
|
787
|
+
end
|
|
619
788
|
|
|
620
|
-
|
|
621
|
-
|
|
789
|
+
ctx = Context.new(
|
|
790
|
+
settings.api_server,
|
|
791
|
+
version: v,
|
|
792
|
+
request: self,
|
|
793
|
+
action: route.action,
|
|
794
|
+
path: route.path,
|
|
795
|
+
args:,
|
|
796
|
+
params:,
|
|
797
|
+
user: current_user,
|
|
798
|
+
endpoint: true,
|
|
799
|
+
resource_path: route.resource_path,
|
|
800
|
+
doc: true
|
|
801
|
+
)
|
|
622
802
|
|
|
623
|
-
|
|
624
|
-
|
|
803
|
+
begin
|
|
804
|
+
desc = route.action.describe(ctx)
|
|
805
|
+
|
|
806
|
+
unless desc
|
|
807
|
+
report_error(403, {}, HaveAPI.message('haveapi.authorization.insufficient_permissions'))
|
|
808
|
+
end
|
|
809
|
+
rescue ValidationError => e
|
|
810
|
+
report_error(400, e.to_hash, e.message_value)
|
|
811
|
+
rescue StandardError => e
|
|
812
|
+
tmp = settings.api_server.call_hooks_for(:description_exception, args: [ctx, e])
|
|
813
|
+
report_error(
|
|
814
|
+
tmp[:http_status] || 500,
|
|
815
|
+
{},
|
|
816
|
+
tmp[:message] || HaveAPI.message('haveapi.errors.server_error')
|
|
817
|
+
)
|
|
625
818
|
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
819
|
|
|
637
|
-
|
|
820
|
+
@formatter.format(true, desc)
|
|
821
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
822
|
+
report_exception(e, ctx)
|
|
823
|
+
end
|
|
638
824
|
end
|
|
639
825
|
end
|
|
640
826
|
|
|
@@ -689,6 +875,13 @@ module HaveAPI
|
|
|
689
875
|
r.describe(hash, context)
|
|
690
876
|
end
|
|
691
877
|
|
|
878
|
+
def parameter_metadata_i18n_items(version: @default_version)
|
|
879
|
+
routes = @routes.fetch(version)
|
|
880
|
+
context = Context.new(self, version:, doc: true)
|
|
881
|
+
|
|
882
|
+
collect_parameter_metadata_i18n_items(routes[:resources], context)
|
|
883
|
+
end
|
|
884
|
+
|
|
692
885
|
def path_for_action(version, action)
|
|
693
886
|
routes = @routes && @routes[version]
|
|
694
887
|
return unless routes
|
|
@@ -64,6 +64,31 @@ module HaveAPI::Spec
|
|
|
64
64
|
opt(:validation_error_http_status, status)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
# Set default response locale.
|
|
68
|
+
def default_locale(locale)
|
|
69
|
+
opt(:default_locale, locale)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Set locales available to the API server.
|
|
73
|
+
def available_locales(locales)
|
|
74
|
+
opt(:available_locales, locales)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Set request header used to negotiate the response locale.
|
|
78
|
+
def locale_header(header)
|
|
79
|
+
opt(:locale_header, header)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Set custom locale resolver.
|
|
83
|
+
def locale(&block)
|
|
84
|
+
opt(:locale, block)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Set the translation scope for action parameter labels/descriptions.
|
|
88
|
+
def parameter_i18n_scope(scope)
|
|
89
|
+
opt(:parameter_i18n_scope, scope)
|
|
90
|
+
end
|
|
91
|
+
|
|
67
92
|
# Set a custom mount path.
|
|
68
93
|
def mount_to(path)
|
|
69
94
|
opt(:mount, path)
|
|
@@ -19,6 +19,16 @@ module HaveAPI::Spec
|
|
|
19
19
|
@api.action_state_auth = asa if asa
|
|
20
20
|
ves = get_opt(:validation_error_http_status)
|
|
21
21
|
@api.validation_error_http_status = ves if ves
|
|
22
|
+
dl = get_opt(:default_locale)
|
|
23
|
+
@api.default_locale = dl if dl
|
|
24
|
+
al = get_opt(:available_locales)
|
|
25
|
+
@api.available_locales = al if al
|
|
26
|
+
lh = get_opt(:locale_header)
|
|
27
|
+
@api.locale_header = lh if lh
|
|
28
|
+
locale = get_opt(:locale)
|
|
29
|
+
@api.locale(&locale) if locale
|
|
30
|
+
parameter_i18n_scope = get_opt(:parameter_i18n_scope)
|
|
31
|
+
@api.parameter_i18n_scope = parameter_i18n_scope if parameter_i18n_scope
|
|
22
32
|
@api.mount(get_opt(:mount) || '/')
|
|
23
33
|
@api.app
|
|
24
34
|
end
|