media_types-serialization 1.0.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +7 -3
- data/.prettierrc +1 -0
- data/CHANGELOG.md +34 -8
- data/CODE_OF_CONDUCT.md +11 -11
- data/Gemfile.lock +137 -143
- data/README.md +182 -91
- data/lib/media_types/serialization.rb +94 -20
- data/lib/media_types/serialization/base.rb +12 -8
- data/lib/media_types/serialization/error.rb +11 -3
- data/lib/media_types/serialization/fake_validator.rb +1 -1
- data/lib/media_types/serialization/serialization_dsl.rb +3 -3
- data/lib/media_types/serialization/serialization_registration.rb +19 -9
- data/lib/media_types/serialization/serializers/problem_serializer.rb +23 -10
- data/lib/media_types/serialization/utils/accept_header.rb +77 -0
- data/lib/media_types/serialization/utils/accept_language_header.rb +82 -0
- data/lib/media_types/serialization/utils/header_list.rb +89 -0
- data/lib/media_types/serialization/version.rb +1 -1
- data/media_types-serialization.gemspec +1 -3
- metadata +12 -42
@@ -15,8 +15,7 @@ require 'active_support/concern'
|
|
15
15
|
require 'active_support/core_ext/module/attribute_accessors'
|
16
16
|
require 'active_support/core_ext/object/blank'
|
17
17
|
|
18
|
-
require '
|
19
|
-
|
18
|
+
require 'media_types/serialization/utils/accept_header'
|
20
19
|
require 'media_types/serialization/base'
|
21
20
|
require 'media_types/serialization/error'
|
22
21
|
require 'media_types/serialization/serialization_dsl'
|
@@ -44,7 +43,8 @@ end
|
|
44
43
|
module MediaTypes
|
45
44
|
module Serialization
|
46
45
|
|
47
|
-
HEADER_ACCEPT = 'HTTP_ACCEPT'
|
46
|
+
HEADER_ACCEPT = 'HTTP_ACCEPT'.freeze
|
47
|
+
HEADER_ACCEPT_LANGUAGE = 'HTTP_ACCEPT_LANGUAGE'.freeze
|
48
48
|
|
49
49
|
mattr_accessor :json_encoder, :json_decoder
|
50
50
|
if defined?(::Oj)
|
@@ -183,7 +183,60 @@ module MediaTypes
|
|
183
183
|
@serialization_output_registrations = @serialization_output_registrations.merge(mergeable_outputs)
|
184
184
|
end
|
185
185
|
end
|
186
|
-
|
186
|
+
|
187
|
+
def allow_output_html(as: nil, view: nil, layout: nil, formats: [:html], variants: nil, **filter_opts)
|
188
|
+
before_action(**filter_opts) do
|
189
|
+
raise SerializersAlreadyFrozenError if defined? @serialization_frozen
|
190
|
+
|
191
|
+
@serialization_output_registrations ||= SerializationRegistration.new(:output)
|
192
|
+
|
193
|
+
html_registration = SerializationRegistration.new(:output)
|
194
|
+
output_identifier = 'text/html'
|
195
|
+
output_identifier += "; variant=#{as}" unless as.nil?
|
196
|
+
|
197
|
+
validator = FakeValidator.new(as.nil? ? 'text/html' : as)
|
198
|
+
|
199
|
+
block = lambda { |_, _, controller|
|
200
|
+
options = {}
|
201
|
+
options[:layout] = layout unless layout.nil?
|
202
|
+
options[:template] = view unless view.nil?
|
203
|
+
options[:formats] = formats unless formats.nil?
|
204
|
+
options[:variants] = variants unless variants.nil?
|
205
|
+
|
206
|
+
controller.render_to_string(**options)
|
207
|
+
}
|
208
|
+
|
209
|
+
html_registration.register_block(nil, validator, nil, block, true, wildcards: true)
|
210
|
+
html_registration.registrations[validator.identifier].display_identifier = output_identifier
|
211
|
+
html_registration.registrations["#{validator.identifier.split('/')[0]}/*"].display_identifier = output_identifier
|
212
|
+
html_registration.registrations['*/*'].display_identifier = output_identifier
|
213
|
+
|
214
|
+
@serialization_output_registrations = @serialization_output_registrations.merge(html_registration)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def allow_output_docs(description, **filter_opts)
|
219
|
+
before_action(**filter_opts) do
|
220
|
+
raise SerializersAlreadyFrozenError if defined? @serialization_frozen
|
221
|
+
|
222
|
+
@serialization_output_registrations ||= SerializationRegistration.new(:output)
|
223
|
+
|
224
|
+
docs_registration = SerializationRegistration.new(:output)
|
225
|
+
validator = FakeValidator.new('text/vnd.delftsolutions.docs')
|
226
|
+
|
227
|
+
block = lambda { |_, _, _|
|
228
|
+
description
|
229
|
+
}
|
230
|
+
|
231
|
+
docs_registration.register_block(nil, validator, nil, block, true, wildcards: true)
|
232
|
+
docs_registration.registrations['text/vnd.delftsolutions.docs'].display_identifier = 'text/plain; charset=utf-8'
|
233
|
+
docs_registration.registrations['text/*'].display_identifier = 'text/plain; charset=utf-8'
|
234
|
+
docs_registration.registrations['*/*'].display_identifier = 'text/plain; charset=utf-8'
|
235
|
+
|
236
|
+
@serialization_output_registrations = @serialization_output_registrations.merge(docs_registration)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
187
240
|
def allow_api_viewer(serializer: MediaTypes::Serialization::Serializers::ApiViewer, **filter_opts)
|
188
241
|
before_action do
|
189
242
|
@serialization_api_viewer_enabled ||= {}
|
@@ -216,7 +269,7 @@ module MediaTypes
|
|
216
269
|
raise ArrayInViewParameterError, :allow_input_serializer if view.is_a? Array
|
217
270
|
views = [view] if views.nil?
|
218
271
|
raise ViewsNotAnArrayError unless views.is_a? Array
|
219
|
-
|
272
|
+
|
220
273
|
before_action do
|
221
274
|
@serialization_available_serializers ||= {}
|
222
275
|
@serialization_available_serializers[:input] ||= {}
|
@@ -259,8 +312,8 @@ module MediaTypes
|
|
259
312
|
##
|
260
313
|
# Freezes additions to the serializes and notifies the controller what it will be able to respond to.
|
261
314
|
#
|
262
|
-
def freeze_io!
|
263
|
-
before_action :serializer_freeze_io_internal
|
315
|
+
def freeze_io!(**filter_opts)
|
316
|
+
before_action :serializer_freeze_io_internal, **filter_opts
|
264
317
|
|
265
318
|
output_error MediaTypes::Serialization::NoInputReceivedError do |p, error|
|
266
319
|
p.title 'Providing input is mandatory. Please set a Content-Type', lang: 'en'
|
@@ -283,11 +336,6 @@ module MediaTypes
|
|
283
336
|
end
|
284
337
|
# rubocop:enable Metrics/BlockLength
|
285
338
|
|
286
|
-
included do
|
287
|
-
protected
|
288
|
-
|
289
|
-
end
|
290
|
-
|
291
339
|
protected
|
292
340
|
|
293
341
|
def serialize(victim, media_type, serializer: Object.new, links: [], vary: ['Accept'])
|
@@ -307,6 +355,7 @@ module MediaTypes
|
|
307
355
|
if obj == MEDIA_TYPES_SERIALIZATION_OBJ_IS_UNDEFINED && block.nil?
|
308
356
|
raise 'render_media was called without an object. Please provide one or supply a block to match the serializer.'
|
309
357
|
end
|
358
|
+
|
310
359
|
obj = nil if obj == MEDIA_TYPES_SERIALIZATION_OBJ_IS_UNDEFINED
|
311
360
|
|
312
361
|
raise SerializersNotFrozenError unless defined? @serialization_frozen
|
@@ -336,10 +385,17 @@ module MediaTypes
|
|
336
385
|
selector.instance_exec(&block)
|
337
386
|
|
338
387
|
raise UnmatchedSerializerError, serializer unless selector.matched
|
388
|
+
|
339
389
|
obj = selector.value
|
340
390
|
end
|
341
391
|
|
342
|
-
serialization_render_resolved(
|
392
|
+
serialization_render_resolved(
|
393
|
+
obj: obj,
|
394
|
+
serializer: serializer,
|
395
|
+
identifier: identifier,
|
396
|
+
registrations: registration,
|
397
|
+
options: options
|
398
|
+
)
|
343
399
|
end
|
344
400
|
|
345
401
|
def deserialize(request)
|
@@ -358,6 +414,7 @@ module MediaTypes
|
|
358
414
|
raise SerializersNotFrozenError unless defined?(@serialization_frozen)
|
359
415
|
raise NoInputReceivedError if request.content_type.blank?
|
360
416
|
raise InputNotAcceptableError unless @serialization_input_registrations.has? request.content_type
|
417
|
+
|
361
418
|
@serialization_input_registrations.call(@serialization_decoded_input, request.content_type, self)
|
362
419
|
end
|
363
420
|
|
@@ -366,8 +423,9 @@ module MediaTypes
|
|
366
423
|
return nil if identifier.nil?
|
367
424
|
|
368
425
|
registration = registration.registrations[identifier]
|
369
|
-
|
426
|
+
|
370
427
|
raise 'Assertion failed, inconsistent answer from resolve_media_type' if registration.nil?
|
428
|
+
|
371
429
|
registration.serializer
|
372
430
|
end
|
373
431
|
|
@@ -375,8 +433,12 @@ module MediaTypes
|
|
375
433
|
|
376
434
|
def resolve_media_type(request, registration, allow_last: true)
|
377
435
|
if defined? @serialization_override_accept
|
378
|
-
|
436
|
+
if allow_last && @serialization_override_accept == 'last'
|
437
|
+
@serialization_override_accept = registration.registrations.keys.last
|
438
|
+
end
|
439
|
+
|
379
440
|
return nil unless registration.has? @serialization_override_accept
|
441
|
+
|
380
442
|
return @serialization_override_accept
|
381
443
|
end
|
382
444
|
|
@@ -386,7 +448,7 @@ module MediaTypes
|
|
386
448
|
#
|
387
449
|
#
|
388
450
|
|
389
|
-
accept_header =
|
451
|
+
accept_header = Utils::AcceptHeader.new(request.get_header(HEADER_ACCEPT)) || ''
|
390
452
|
accept_header.each do |mime_type|
|
391
453
|
stripped = mime_type.to_s.split(';')[0]
|
392
454
|
next unless registration.has? stripped
|
@@ -403,7 +465,7 @@ module MediaTypes
|
|
403
465
|
identifier = serializer.validator.identifier
|
404
466
|
obj = { request: request, registrations: registrations }
|
405
467
|
new_registrations = serializer.outputs_for(views: [nil])
|
406
|
-
|
468
|
+
|
407
469
|
serialization_render_resolved(obj: obj, serializer: serializer, identifier: identifier, registrations: new_registrations, options: {})
|
408
470
|
response.status = :not_acceptable
|
409
471
|
end
|
@@ -424,7 +486,10 @@ module MediaTypes
|
|
424
486
|
input_is_allowed = @serialization_input_registrations.has? request.content_type unless request.content_type.blank?
|
425
487
|
|
426
488
|
unless input_is_allowed || all_allowed
|
427
|
-
serializers = @serialization_unsupported_media_type_serializer || [
|
489
|
+
serializers = @serialization_unsupported_media_type_serializer || [
|
490
|
+
MediaTypes::Serialization::Serializers::ProblemSerializer,
|
491
|
+
MediaTypes::Serialization::Serializers::FallbackUnsupportedMediaTypeSerializer
|
492
|
+
]
|
428
493
|
registrations = SerializationRegistration.new(:output)
|
429
494
|
serializers.each do |s|
|
430
495
|
registrations = registrations.merge(s.outputs_for(views: [nil, :html]))
|
@@ -452,7 +517,10 @@ module MediaTypes
|
|
452
517
|
input_data = request.body.read
|
453
518
|
@serialization_decoded_input = @serialization_input_registrations.decode(input_data, request.content_type, self)
|
454
519
|
rescue InputValidationFailedError => e
|
455
|
-
serializers = @serialization_input_validation_failed_serializer || [
|
520
|
+
serializers = @serialization_input_validation_failed_serializer || [
|
521
|
+
MediaTypes::Serialization::Serializers::ProblemSerializer,
|
522
|
+
MediaTypes::Serialization::Serializers::InputValidationErrorSerializer
|
523
|
+
]
|
456
524
|
registrations = SerializationRegistration.new(:output)
|
457
525
|
serializers.each do |s|
|
458
526
|
registrations = registrations.merge(s.outputs_for(views: [nil, :html]))
|
@@ -498,7 +566,13 @@ module MediaTypes
|
|
498
566
|
actions: @serialization_available_serializers,
|
499
567
|
}
|
500
568
|
|
501
|
-
serialization_render_resolved
|
569
|
+
serialization_render_resolved(
|
570
|
+
obj: input,
|
571
|
+
serializer: description_serializer,
|
572
|
+
identifier: endpoint_matched_identifier,
|
573
|
+
registrations: @serialization_output_registrations,
|
574
|
+
options: {}
|
575
|
+
)
|
502
576
|
return
|
503
577
|
end
|
504
578
|
|
@@ -30,6 +30,10 @@ module MediaTypes
|
|
30
30
|
self.serializer_disable_wildcards = true
|
31
31
|
end
|
32
32
|
|
33
|
+
def enable_wildcards
|
34
|
+
self.serializer_disable_wildcards = false
|
35
|
+
end
|
36
|
+
|
33
37
|
def output(view: nil, version: nil, versions: nil, &block)
|
34
38
|
versions = [version] if versions.nil?
|
35
39
|
raise VersionsNotAnArrayError unless versions.is_a? Array
|
@@ -57,18 +61,18 @@ module MediaTypes
|
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
|
-
def output_alias(media_type_identifier, view: nil)
|
64
|
+
def output_alias(media_type_identifier, view: nil, hide_variant: false)
|
61
65
|
validator = serializer_validator.view(view)
|
62
66
|
victim_identifier = validator.identifier
|
63
67
|
|
64
|
-
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, false, wildcards: !self.serializer_disable_wildcards)
|
68
|
+
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, false, hide_variant, wildcards: !self.serializer_disable_wildcards)
|
65
69
|
end
|
66
70
|
|
67
|
-
def output_alias_optional(media_type_identifier, view: nil)
|
71
|
+
def output_alias_optional(media_type_identifier, view: nil, hide_variant: false)
|
68
72
|
validator = serializer_validator.view(view)
|
69
73
|
victim_identifier = validator.identifier
|
70
74
|
|
71
|
-
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, true, wildcards: !self.serializer_disable_wildcards)
|
75
|
+
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, true, hide_variant, wildcards: !self.serializer_disable_wildcards)
|
72
76
|
end
|
73
77
|
|
74
78
|
def input(view: nil, version: nil, versions: nil, &block)
|
@@ -102,22 +106,22 @@ module MediaTypes
|
|
102
106
|
validator = serializer_validator.view(view)
|
103
107
|
victim_identifier = validator.identifier
|
104
108
|
|
105
|
-
serializer_input_registration.register_alias(self, media_type_identifier, victim_identifier, false)
|
109
|
+
serializer_input_registration.register_alias(self, media_type_identifier, victim_identifier, false, true, wildcards: false)
|
106
110
|
end
|
107
111
|
|
108
112
|
def input_alias_optional(media_type_identifier, view: nil)
|
109
113
|
validator = serializer_validator.view(view)
|
110
114
|
victim_identifier = validator.identifier
|
111
115
|
|
112
|
-
serializer_input_registration.register_alias(self, media_type_identifier, victim_identifier, true)
|
116
|
+
serializer_input_registration.register_alias(self, media_type_identifier, victim_identifier, true, true, wildcards: false)
|
113
117
|
end
|
114
118
|
|
115
|
-
def serialize(victim, media_type_identifier, context
|
119
|
+
def serialize(victim, media_type_identifier, context:, dsl: nil, raw: nil)
|
116
120
|
dsl ||= SerializationDSL.new(self, context: context)
|
117
121
|
serializer_output_registration.call(victim, media_type_identifier.to_s, context, dsl: dsl, raw: raw)
|
118
122
|
end
|
119
123
|
|
120
|
-
def deserialize(victim, media_type_identifier, context)
|
124
|
+
def deserialize(victim, media_type_identifier, context:)
|
121
125
|
serializer_input_registration.call(victim, media_type_identifier, context)
|
122
126
|
end
|
123
127
|
|
@@ -73,21 +73,29 @@ module MediaTypes
|
|
73
73
|
def initialize(identifier, inout)
|
74
74
|
super(
|
75
75
|
"Serializer tried to define an #{inout}_alias that points to the media type identifier #{identifier} but no such #{inout} has been defined yet. Please move the #{inout} definition above the alias.\n\n" \
|
76
|
-
"Move the
|
76
|
+
"Move the #{inout} definition above the alias:\n" \
|
77
77
|
"\n" \
|
78
78
|
"class MySerializer < MediaTypes::Serialization::Base\n" \
|
79
79
|
"#...\n" \
|
80
|
-
"
|
80
|
+
"#{inout} do\n" \
|
81
81
|
" # ...\n" \
|
82
82
|
"end\n" \
|
83
83
|
"\n" \
|
84
|
-
"
|
84
|
+
"#{inout}_alias 'text/html'\n" \
|
85
85
|
"# ^----- move here\n" \
|
86
86
|
'end'
|
87
87
|
)
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
class VersionedAliasDefinitionError < ConfigurationError
|
92
|
+
def initialize(identifier, inout, prefix_match)
|
93
|
+
super(
|
94
|
+
"Serializer tried to define an #{inout}_alias that points to the media type identifier #{identifier} but no such #{inout} has been defined yet. An #{inout} named #{prefix_match} was found. Often this can be fixed by providing an #{inout} with a nil version."
|
95
|
+
)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
91
99
|
class DuplicateDefinitionError < ConfigurationError
|
92
100
|
def initialize(identifier, inout)
|
93
101
|
super("Serializer tried to define an #{inout} using the media type identifier #{identifier}, but another #{inout} was already defined with that identifier. Please remove one of the two.")
|
@@ -27,8 +27,8 @@ class FakeValidator
|
|
27
27
|
def identifier
|
28
28
|
suffix = suffixes[[internal_view, internal_version]] || ''
|
29
29
|
result = prefix
|
30
|
-
result += '.' + internal_view.to_s unless internal_view.nil?
|
31
30
|
result += '.v' + internal_version.to_s unless internal_version.nil?
|
31
|
+
result += '.' + internal_view.to_s unless internal_view.nil?
|
32
32
|
result += '+' + suffix.to_s unless suffix.empty?
|
33
33
|
|
34
34
|
result
|
@@ -56,7 +56,7 @@ module MediaTypes
|
|
56
56
|
array.each do |e|
|
57
57
|
child_links = []
|
58
58
|
context = SerializationDSL.new(__getobj__, child_links, context: @serialization_context)
|
59
|
-
serializer.serialize(e, identifier, @serialization_context, dsl: context)
|
59
|
+
serializer.serialize(e, identifier, context: @serialization_context, dsl: context)
|
60
60
|
|
61
61
|
self_links = child_links.select { |l| l[:rel] == :self }
|
62
62
|
raise NoSelfLinkProvidedError, identifier unless self_links.any?
|
@@ -79,7 +79,7 @@ module MediaTypes
|
|
79
79
|
|
80
80
|
array.each do |e|
|
81
81
|
context = SerializationDSL.new(__getobj__, [], @serialization_vary, context: @serialization_context)
|
82
|
-
result = serializer.serialize(e, identifier, @serialization_context, dsl: context, raw: true)
|
82
|
+
result = serializer.serialize(e, identifier, context: @serialization_context, dsl: context, raw: true)
|
83
83
|
|
84
84
|
result = block.call(result) unless block.nil?
|
85
85
|
|
@@ -105,7 +105,7 @@ module MediaTypes
|
|
105
105
|
def emit
|
106
106
|
serialization_dsl_result
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
def object(&block)
|
110
110
|
context = SerializationDSL.new(__getobj__, @serialization_links, @serialization_vary, context: @serialization_context)
|
111
111
|
context.instance_exec(&block)
|
@@ -31,14 +31,23 @@ module MediaTypes
|
|
31
31
|
register_wildcards(identifier, registration) if wildcards && inout == :output
|
32
32
|
end
|
33
33
|
|
34
|
-
def register_alias(serializer, alias_identifier, target_identifier, optional, wildcards: true)
|
35
|
-
raise DuplicateDefinitionError.new(
|
34
|
+
def register_alias(serializer, alias_identifier, target_identifier, optional, hide_variant, wildcards: true)
|
35
|
+
raise DuplicateDefinitionError.new(alias_identifier, inout) if registrations.key? alias_identifier
|
36
36
|
|
37
|
-
|
37
|
+
unless registrations.key? target_identifier
|
38
|
+
potential_match = registrations.keys.find do |k|
|
39
|
+
k.starts_with? target_identifier
|
40
|
+
end
|
41
|
+
raise VersionedAliasDefinitionError.new(target_identifier, inout, potential_match) unless potential_match.nil?
|
42
|
+
raise UnbackedAliasDefinitionError.new(target_identifier, inout)
|
43
|
+
end
|
38
44
|
|
39
45
|
target = registrations[target_identifier]
|
40
46
|
|
41
|
-
|
47
|
+
result_content_type = alias_identifier
|
48
|
+
result_content_type += "; variant=#{target_identifier}" unless hide_variant
|
49
|
+
|
50
|
+
registration = SerializationAliasRegistration.new serializer, inout, target.validator, result_content_type, target, optional, hide_variant
|
42
51
|
registrations[alias_identifier] = registration
|
43
52
|
|
44
53
|
register_wildcards(alias_identifier, registration) if wildcards && inout == :output
|
@@ -103,7 +112,7 @@ module MediaTypes
|
|
103
112
|
private
|
104
113
|
|
105
114
|
def register_wildcards(identifier, registration)
|
106
|
-
new_alias = SerializationAliasRegistration.new registration.serializer, registration.inout, registration.validator, identifier, registration, true
|
115
|
+
new_alias = SerializationAliasRegistration.new registration.serializer, registration.inout, registration.validator, identifier, registration, true, true
|
107
116
|
|
108
117
|
registrations['*/*'] = new_alias unless has? '*/*'
|
109
118
|
|
@@ -160,14 +169,14 @@ module MediaTypes
|
|
160
169
|
begin
|
161
170
|
victim = MediaTypes::Serialization.json_decoder.call(victim)
|
162
171
|
validator.validate!(victim)
|
163
|
-
rescue MediaTypes::Scheme::ValidationError, Oj::ParseError, JSON::ParserError => inner
|
172
|
+
rescue MediaTypes::Scheme::ValidationError, Oj::ParseError, JSON::ParserError, EncodingError => inner
|
164
173
|
raise InputValidationFailedError, inner
|
165
174
|
end
|
166
175
|
else
|
167
176
|
begin
|
168
177
|
victim = MediaTypes::Serialization.json_decoder.call(victim)
|
169
178
|
validator.validate!(victim)
|
170
|
-
rescue MediaTypes::Scheme::ValidationError, JSON::ParserError => inner
|
179
|
+
rescue MediaTypes::Scheme::ValidationError, JSON::ParserError, EncodingError => inner
|
171
180
|
raise InputValidationFailedError, inner
|
172
181
|
end
|
173
182
|
end
|
@@ -204,9 +213,10 @@ module MediaTypes
|
|
204
213
|
|
205
214
|
# A registration that calls another registration when called.
|
206
215
|
class SerializationAliasRegistration < SerializationBaseRegistration
|
207
|
-
def initialize(serializer, inout, validator, display_identifier, target, optional)
|
216
|
+
def initialize(serializer, inout, validator, display_identifier, target, optional, hide_variant)
|
208
217
|
self.target = target
|
209
218
|
self.optional = optional
|
219
|
+
self.hide_variant = hide_variant
|
210
220
|
super(serializer, inout, validator, display_identifier)
|
211
221
|
end
|
212
222
|
|
@@ -229,7 +239,7 @@ module MediaTypes
|
|
229
239
|
target.call(victim, context, dsl: dsl, raw: raw)
|
230
240
|
end
|
231
241
|
|
232
|
-
attr_accessor :target, :optional
|
242
|
+
attr_accessor :target, :optional, :hide_variant
|
233
243
|
end
|
234
244
|
end
|
235
245
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'erb'
|
4
4
|
require 'media_types/serialization/base'
|
5
|
+
require 'media_types/serialization/utils/accept_language_header'
|
5
6
|
|
6
7
|
module MediaTypes
|
7
8
|
module Serialization
|
@@ -9,13 +10,19 @@ module MediaTypes
|
|
9
10
|
class ProblemSerializer < MediaTypes::Serialization::Base
|
10
11
|
|
11
12
|
unvalidated 'application/vnd.delftsolutions.problem'
|
13
|
+
disable_wildcards
|
12
14
|
|
13
15
|
output do |problem, _, context|
|
14
16
|
raise 'No translations defined, add at least one title' unless problem.translations.keys.any?
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
accept_language_header = Utils::AcceptLanguageHeader.new(context.request.get_header(HEADER_ACCEPT_LANGUAGE) || '')
|
19
|
+
translation_entry = accept_language_header.map do |locale|
|
20
|
+
problem.translations.keys.find do |l|
|
21
|
+
l.start_with? locale.locale
|
22
|
+
end
|
23
|
+
end.compact.first || problem.translations.keys.first
|
24
|
+
translation = problem.translations[translation_entry]
|
25
|
+
|
19
26
|
title = translation[:title]
|
20
27
|
detail = translation[:detail] || problem.error.message
|
21
28
|
|
@@ -33,12 +40,19 @@ module MediaTypes
|
|
33
40
|
output_alias 'application/problem+json'
|
34
41
|
|
35
42
|
output_raw view: :html do |problem, _, context|
|
36
|
-
|
37
|
-
|
38
|
-
|
43
|
+
accept_language_header = Utils::AcceptLanguageHeader.new(context.request.get_header(HEADER_ACCEPT_LANGUAGE) || '')
|
44
|
+
translation_entry = accept_language_header.map do |locale|
|
45
|
+
problem.translations.keys.find do |l|
|
46
|
+
l.starts_with? locale.locale
|
47
|
+
end
|
48
|
+
end.compact.first || problem.translations.keys.first
|
49
|
+
translation = problem.translations[translation_entry]
|
50
|
+
|
39
51
|
title = translation[:title]
|
40
52
|
detail = translation[:detail] || problem.error.message
|
41
53
|
|
54
|
+
detail_lang = translation[:detail].nil? ? 'en' : translation_entry
|
55
|
+
|
42
56
|
input = OpenStruct.new(
|
43
57
|
title: title,
|
44
58
|
detail: detail,
|
@@ -61,11 +75,11 @@ module MediaTypes
|
|
61
75
|
</header>
|
62
76
|
<section id="content">
|
63
77
|
<nav>
|
64
|
-
<section id="description">
|
78
|
+
<section id="description" lang="#{translation_entry}">
|
65
79
|
<h2><a href="<%= help_url %>"><%= CGI::escapeHTML(title) %></a></h2>
|
66
80
|
</section>
|
67
81
|
</nav>
|
68
|
-
<main>
|
82
|
+
<main lang="#{detail_lang}">
|
69
83
|
<p><%= detail %>
|
70
84
|
</main>
|
71
85
|
</section>
|
@@ -76,8 +90,7 @@ module MediaTypes
|
|
76
90
|
template.result(input.instance_eval { binding })
|
77
91
|
end
|
78
92
|
|
79
|
-
|
80
|
-
self.serializer_output_registration.registrations.delete('*/*')
|
93
|
+
enable_wildcards
|
81
94
|
|
82
95
|
output_alias_optional 'text/html', view: :html
|
83
96
|
|