media_types-serialization 1.0.2 → 1.3.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/.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
|
|