active_model_serializers_custom 0.10.90
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 +7 -0
- data/.github/ISSUE_TEMPLATE.md +29 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- data/.gitignore +35 -0
- data/.rubocop.yml +109 -0
- data/.simplecov +110 -0
- data/.travis.yml +63 -0
- data/CHANGELOG.md +727 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +105 -0
- data/Gemfile +74 -0
- data/MIT-LICENSE +22 -0
- data/README.md +305 -0
- data/Rakefile +76 -0
- data/active_model_serializers.gemspec +64 -0
- data/appveyor.yml +28 -0
- data/bin/bench +171 -0
- data/bin/bench_regression +316 -0
- data/bin/rubocop +38 -0
- data/bin/serve_benchmark +39 -0
- data/docs/README.md +41 -0
- data/docs/STYLE.md +58 -0
- data/docs/general/adapters.md +269 -0
- data/docs/general/caching.md +58 -0
- data/docs/general/configuration_options.md +185 -0
- data/docs/general/deserialization.md +100 -0
- data/docs/general/fields.md +31 -0
- data/docs/general/getting_started.md +133 -0
- data/docs/general/instrumentation.md +40 -0
- data/docs/general/key_transforms.md +40 -0
- data/docs/general/logging.md +21 -0
- data/docs/general/rendering.md +293 -0
- data/docs/general/serializers.md +495 -0
- data/docs/how-open-source-maintained.jpg +0 -0
- data/docs/howto/add_pagination_links.md +138 -0
- data/docs/howto/add_relationship_links.md +140 -0
- data/docs/howto/add_root_key.md +62 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +66 -0
- data/docs/howto/passing_arbitrary_options.md +27 -0
- data/docs/howto/serialize_poro.md +73 -0
- data/docs/howto/test.md +154 -0
- data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
- data/docs/integrations/ember-and-json-api.md +147 -0
- data/docs/integrations/grape.md +19 -0
- data/docs/jsonapi/errors.md +56 -0
- data/docs/jsonapi/schema.md +151 -0
- data/docs/jsonapi/schema/schema.json +366 -0
- data/docs/rfcs/0000-namespace.md +106 -0
- data/docs/rfcs/template.md +15 -0
- data/lib/action_controller/serialization.rb +76 -0
- data/lib/active_model/serializable_resource.rb +13 -0
- data/lib/active_model/serializer.rb +418 -0
- data/lib/active_model/serializer/adapter.rb +26 -0
- data/lib/active_model/serializer/adapter/attributes.rb +17 -0
- data/lib/active_model/serializer/adapter/base.rb +20 -0
- data/lib/active_model/serializer/adapter/json.rb +17 -0
- data/lib/active_model/serializer/adapter/json_api.rb +17 -0
- data/lib/active_model/serializer/adapter/null.rb +17 -0
- data/lib/active_model/serializer/array_serializer.rb +14 -0
- data/lib/active_model/serializer/association.rb +91 -0
- data/lib/active_model/serializer/attribute.rb +27 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
- data/lib/active_model/serializer/collection_serializer.rb +90 -0
- data/lib/active_model/serializer/concerns/caching.rb +304 -0
- data/lib/active_model/serializer/error_serializer.rb +16 -0
- data/lib/active_model/serializer/errors_serializer.rb +34 -0
- data/lib/active_model/serializer/field.rb +92 -0
- data/lib/active_model/serializer/fieldset.rb +33 -0
- data/lib/active_model/serializer/has_many_reflection.rb +12 -0
- data/lib/active_model/serializer/has_one_reflection.rb +9 -0
- data/lib/active_model/serializer/lazy_association.rb +99 -0
- data/lib/active_model/serializer/link.rb +23 -0
- data/lib/active_model/serializer/lint.rb +152 -0
- data/lib/active_model/serializer/null.rb +19 -0
- data/lib/active_model/serializer/reflection.rb +212 -0
- data/lib/active_model/serializer/version.rb +7 -0
- data/lib/active_model_serializers.rb +63 -0
- data/lib/active_model_serializers/adapter.rb +100 -0
- data/lib/active_model_serializers/adapter/attributes.rb +15 -0
- data/lib/active_model_serializers/adapter/base.rb +85 -0
- data/lib/active_model_serializers/adapter/json.rb +23 -0
- data/lib/active_model_serializers/adapter/json_api.rb +535 -0
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
- data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
- data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
- data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
- data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
- data/lib/active_model_serializers/adapter/null.rb +11 -0
- data/lib/active_model_serializers/callbacks.rb +57 -0
- data/lib/active_model_serializers/deprecate.rb +56 -0
- data/lib/active_model_serializers/deserialization.rb +17 -0
- data/lib/active_model_serializers/json_pointer.rb +16 -0
- data/lib/active_model_serializers/logging.rb +124 -0
- data/lib/active_model_serializers/lookup_chain.rb +82 -0
- data/lib/active_model_serializers/model.rb +132 -0
- data/lib/active_model_serializers/railtie.rb +52 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
- data/lib/active_model_serializers/serializable_resource.rb +84 -0
- data/lib/active_model_serializers/serialization_context.rb +41 -0
- data/lib/active_model_serializers/test.rb +9 -0
- data/lib/active_model_serializers/test/schema.rb +140 -0
- data/lib/active_model_serializers/test/serializer.rb +127 -0
- data/lib/generators/rails/USAGE +6 -0
- data/lib/generators/rails/resource_override.rb +12 -0
- data/lib/generators/rails/serializer_generator.rb +38 -0
- data/lib/generators/rails/templates/serializer.rb.erb +8 -0
- data/lib/grape/active_model_serializers.rb +18 -0
- data/lib/grape/formatters/active_model_serializers.rb +34 -0
- data/lib/grape/helpers/active_model_serializers.rb +19 -0
- data/lib/tasks/rubocop.rake +55 -0
- data/test/action_controller/adapter_selector_test.rb +64 -0
- data/test/action_controller/explicit_serializer_test.rb +137 -0
- data/test/action_controller/json/include_test.rb +248 -0
- data/test/action_controller/json_api/deserialization_test.rb +114 -0
- data/test/action_controller/json_api/errors_test.rb +42 -0
- data/test/action_controller/json_api/fields_test.rb +68 -0
- data/test/action_controller/json_api/linked_test.rb +204 -0
- data/test/action_controller/json_api/pagination_test.rb +126 -0
- data/test/action_controller/json_api/transform_test.rb +191 -0
- data/test/action_controller/lookup_proc_test.rb +51 -0
- data/test/action_controller/namespace_lookup_test.rb +239 -0
- data/test/action_controller/serialization_scope_name_test.rb +237 -0
- data/test/action_controller/serialization_test.rb +480 -0
- data/test/active_model_serializers/adapter_for_test.rb +210 -0
- data/test/active_model_serializers/json_pointer_test.rb +24 -0
- data/test/active_model_serializers/logging_test.rb +79 -0
- data/test/active_model_serializers/model_test.rb +144 -0
- data/test/active_model_serializers/railtie_test_isolated.rb +70 -0
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +73 -0
- data/test/active_model_serializers/test/schema_test.rb +133 -0
- data/test/active_model_serializers/test/serializer_test.rb +64 -0
- data/test/active_record_test.rb +11 -0
- data/test/adapter/attributes_test.rb +42 -0
- data/test/adapter/deprecation_test.rb +102 -0
- data/test/adapter/json/belongs_to_test.rb +47 -0
- data/test/adapter/json/collection_test.rb +106 -0
- data/test/adapter/json/has_many_test.rb +55 -0
- data/test/adapter/json/transform_test.rb +95 -0
- data/test/adapter/json_api/belongs_to_test.rb +157 -0
- data/test/adapter/json_api/collection_test.rb +98 -0
- data/test/adapter/json_api/errors_test.rb +78 -0
- data/test/adapter/json_api/fields_test.rb +98 -0
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
- data/test/adapter/json_api/has_many_test.rb +175 -0
- data/test/adapter/json_api/has_one_test.rb +82 -0
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
- data/test/adapter/json_api/json_api_test.rb +35 -0
- data/test/adapter/json_api/linked_test.rb +415 -0
- data/test/adapter/json_api/links_test.rb +112 -0
- data/test/adapter/json_api/pagination_links_test.rb +208 -0
- data/test/adapter/json_api/parse_test.rb +139 -0
- data/test/adapter/json_api/relationship_test.rb +399 -0
- data/test/adapter/json_api/resource_meta_test.rb +102 -0
- data/test/adapter/json_api/toplevel_jsonapi_test.rb +84 -0
- data/test/adapter/json_api/transform_test.rb +514 -0
- data/test/adapter/json_api/type_test.rb +195 -0
- data/test/adapter/json_test.rb +48 -0
- data/test/adapter/null_test.rb +24 -0
- data/test/adapter/polymorphic_test.rb +220 -0
- data/test/adapter_test.rb +69 -0
- data/test/array_serializer_test.rb +24 -0
- data/test/benchmark/app.rb +67 -0
- data/test/benchmark/benchmarking_support.rb +69 -0
- data/test/benchmark/bm_active_record.rb +83 -0
- data/test/benchmark/bm_adapter.rb +40 -0
- data/test/benchmark/bm_caching.rb +121 -0
- data/test/benchmark/bm_lookup_chain.rb +85 -0
- data/test/benchmark/bm_transform.rb +47 -0
- data/test/benchmark/config.ru +3 -0
- data/test/benchmark/controllers.rb +85 -0
- data/test/benchmark/fixtures.rb +221 -0
- data/test/cache_test.rb +717 -0
- data/test/collection_serializer_test.rb +129 -0
- data/test/fixtures/active_record.rb +115 -0
- data/test/fixtures/poro.rb +227 -0
- data/test/generators/scaffold_controller_generator_test.rb +26 -0
- data/test/generators/serializer_generator_test.rb +77 -0
- data/test/grape_test.rb +198 -0
- data/test/lint_test.rb +51 -0
- data/test/logger_test.rb +22 -0
- data/test/poro_test.rb +11 -0
- data/test/serializable_resource_test.rb +81 -0
- data/test/serializers/association_macros_test.rb +39 -0
- data/test/serializers/associations_test.rb +520 -0
- data/test/serializers/attribute_test.rb +155 -0
- data/test/serializers/attributes_test.rb +54 -0
- data/test/serializers/caching_configuration_test_isolated.rb +172 -0
- data/test/serializers/configuration_test.rb +34 -0
- data/test/serializers/fieldset_test.rb +16 -0
- data/test/serializers/meta_test.rb +204 -0
- data/test/serializers/options_test.rb +34 -0
- data/test/serializers/read_attribute_for_serialization_test.rb +81 -0
- data/test/serializers/reflection_test.rb +481 -0
- data/test/serializers/root_test.rb +23 -0
- data/test/serializers/serialization_test.rb +57 -0
- data/test/serializers/serializer_for_test.rb +138 -0
- data/test/serializers/serializer_for_with_namespace_test.rb +90 -0
- data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/isolated_unit.rb +86 -0
- data/test/support/rails5_shims.rb +55 -0
- data/test/support/rails_app.rb +40 -0
- data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
- data/test/support/schemas/custom/show.json +7 -0
- data/test/support/schemas/hyper_schema.json +93 -0
- data/test/support/schemas/render_using_json_api.json +43 -0
- data/test/support/schemas/simple_json_pointers.json +10 -0
- data/test/support/serialization_testing.rb +81 -0
- data/test/test_helper.rb +72 -0
- metadata +622 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveModelSerializers
|
|
4
|
+
module Adapter
|
|
5
|
+
class JsonApi
|
|
6
|
+
# NOTE(Experimental):
|
|
7
|
+
# This is an experimental feature. Both the interface and internals could be subject
|
|
8
|
+
# to changes.
|
|
9
|
+
module Deserialization
|
|
10
|
+
InvalidDocument = Class.new(ArgumentError)
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# Transform a JSON API document, containing a single data object,
|
|
15
|
+
# into a hash that is ready for ActiveRecord::Base.new() and such.
|
|
16
|
+
# Raises InvalidDocument if the payload is not properly formatted.
|
|
17
|
+
#
|
|
18
|
+
# @param [Hash|ActionController::Parameters] document
|
|
19
|
+
# @param [Hash] options
|
|
20
|
+
# only: Array of symbols of whitelisted fields.
|
|
21
|
+
# except: Array of symbols of blacklisted fields.
|
|
22
|
+
# keys: Hash of translated keys (e.g. :author => :user).
|
|
23
|
+
# polymorphic: Array of symbols of polymorphic fields.
|
|
24
|
+
# @return [Hash]
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# document = {
|
|
28
|
+
# data: {
|
|
29
|
+
# id: 1,
|
|
30
|
+
# type: 'post',
|
|
31
|
+
# attributes: {
|
|
32
|
+
# title: 'Title 1',
|
|
33
|
+
# date: '2015-12-20'
|
|
34
|
+
# },
|
|
35
|
+
# associations: {
|
|
36
|
+
# author: {
|
|
37
|
+
# data: {
|
|
38
|
+
# type: 'user',
|
|
39
|
+
# id: 2
|
|
40
|
+
# }
|
|
41
|
+
# },
|
|
42
|
+
# second_author: {
|
|
43
|
+
# data: nil
|
|
44
|
+
# },
|
|
45
|
+
# comments: {
|
|
46
|
+
# data: [{
|
|
47
|
+
# type: 'comment',
|
|
48
|
+
# id: 3
|
|
49
|
+
# },{
|
|
50
|
+
# type: 'comment',
|
|
51
|
+
# id: 4
|
|
52
|
+
# }]
|
|
53
|
+
# }
|
|
54
|
+
# }
|
|
55
|
+
# }
|
|
56
|
+
# }
|
|
57
|
+
#
|
|
58
|
+
# parse(document) #=>
|
|
59
|
+
# # {
|
|
60
|
+
# # title: 'Title 1',
|
|
61
|
+
# # date: '2015-12-20',
|
|
62
|
+
# # author_id: 2,
|
|
63
|
+
# # second_author_id: nil
|
|
64
|
+
# # comment_ids: [3, 4]
|
|
65
|
+
# # }
|
|
66
|
+
#
|
|
67
|
+
# parse(document, only: [:title, :date, :author],
|
|
68
|
+
# keys: { date: :published_at },
|
|
69
|
+
# polymorphic: [:author]) #=>
|
|
70
|
+
# # {
|
|
71
|
+
# # title: 'Title 1',
|
|
72
|
+
# # published_at: '2015-12-20',
|
|
73
|
+
# # author_id: '2',
|
|
74
|
+
# # author_type: 'people'
|
|
75
|
+
# # }
|
|
76
|
+
#
|
|
77
|
+
def parse!(document, options = {})
|
|
78
|
+
parse(document, options) do |invalid_payload, reason|
|
|
79
|
+
fail InvalidDocument, "Invalid payload (#{reason}): #{invalid_payload}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Same as parse!, but returns an empty hash instead of raising InvalidDocument
|
|
84
|
+
# on invalid payloads.
|
|
85
|
+
def parse(document, options = {})
|
|
86
|
+
document = document.dup.permit!.to_h if document.is_a?(ActionController::Parameters)
|
|
87
|
+
|
|
88
|
+
validate_payload(document) do |invalid_document, reason|
|
|
89
|
+
yield invalid_document, reason if block_given?
|
|
90
|
+
return {}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
primary_data = document['data']
|
|
94
|
+
attributes = primary_data['attributes'] || {}
|
|
95
|
+
attributes['id'] = primary_data['id'] if primary_data['id']
|
|
96
|
+
relationships = primary_data['relationships'] || {}
|
|
97
|
+
|
|
98
|
+
filter_fields(attributes, options)
|
|
99
|
+
filter_fields(relationships, options)
|
|
100
|
+
|
|
101
|
+
hash = {}
|
|
102
|
+
hash.merge!(parse_attributes(attributes, options))
|
|
103
|
+
hash.merge!(parse_relationships(relationships, options))
|
|
104
|
+
|
|
105
|
+
hash
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Checks whether a payload is compliant with the JSON API spec.
|
|
109
|
+
#
|
|
110
|
+
# @api private
|
|
111
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
112
|
+
def validate_payload(payload)
|
|
113
|
+
unless payload.is_a?(Hash)
|
|
114
|
+
yield payload, 'Expected hash'
|
|
115
|
+
return
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
primary_data = payload['data']
|
|
119
|
+
unless primary_data.is_a?(Hash)
|
|
120
|
+
yield payload, { data: 'Expected hash' }
|
|
121
|
+
return
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
attributes = primary_data['attributes'] || {}
|
|
125
|
+
unless attributes.is_a?(Hash)
|
|
126
|
+
yield payload, { data: { attributes: 'Expected hash or nil' } }
|
|
127
|
+
return
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
relationships = primary_data['relationships'] || {}
|
|
131
|
+
unless relationships.is_a?(Hash)
|
|
132
|
+
yield payload, { data: { relationships: 'Expected hash or nil' } }
|
|
133
|
+
return
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
relationships.each do |(key, value)|
|
|
137
|
+
unless value.is_a?(Hash) && value.key?('data')
|
|
138
|
+
yield payload, { data: { relationships: { key => 'Expected hash with :data key' } } }
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
143
|
+
|
|
144
|
+
# @api private
|
|
145
|
+
def filter_fields(fields, options)
|
|
146
|
+
if (only = options[:only])
|
|
147
|
+
fields.slice!(*Array(only).map(&:to_s))
|
|
148
|
+
elsif (except = options[:except])
|
|
149
|
+
fields.except!(*Array(except).map(&:to_s))
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# @api private
|
|
154
|
+
def field_key(field, options)
|
|
155
|
+
(options[:keys] || {}).fetch(field.to_sym, field).to_sym
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# @api private
|
|
159
|
+
def parse_attributes(attributes, options)
|
|
160
|
+
transform_keys(attributes, options)
|
|
161
|
+
.map { |(k, v)| { field_key(k, options) => v } }
|
|
162
|
+
.reduce({}, :merge)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Given an association name, and a relationship data attribute, build a hash
|
|
166
|
+
# mapping the corresponding ActiveRecord attribute to the corresponding value.
|
|
167
|
+
#
|
|
168
|
+
# @example
|
|
169
|
+
# parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' },
|
|
170
|
+
# { 'id' => '2', 'type' => 'comments' }],
|
|
171
|
+
# {})
|
|
172
|
+
# # => { :comment_ids => ['1', '2'] }
|
|
173
|
+
# parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {})
|
|
174
|
+
# # => { :author_id => '1' }
|
|
175
|
+
# parse_relationship(:author, nil, {})
|
|
176
|
+
# # => { :author_id => nil }
|
|
177
|
+
# @param [Symbol] assoc_name
|
|
178
|
+
# @param [Hash] assoc_data
|
|
179
|
+
# @param [Hash] options
|
|
180
|
+
# @return [Hash{Symbol, Object}]
|
|
181
|
+
#
|
|
182
|
+
# @api private
|
|
183
|
+
def parse_relationship(assoc_name, assoc_data, options)
|
|
184
|
+
prefix_key = field_key(assoc_name, options).to_s.singularize
|
|
185
|
+
hash =
|
|
186
|
+
if assoc_data.is_a?(Array)
|
|
187
|
+
{ "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } }
|
|
188
|
+
else
|
|
189
|
+
{ "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
|
|
193
|
+
if polymorphic
|
|
194
|
+
hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'].classify : nil
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
hash
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# @api private
|
|
201
|
+
def parse_relationships(relationships, options)
|
|
202
|
+
transform_keys(relationships, options)
|
|
203
|
+
.map { |(k, v)| parse_relationship(k, v['data'], options) }
|
|
204
|
+
.reduce({}, :merge)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @api private
|
|
208
|
+
def transform_keys(hash, options)
|
|
209
|
+
transform = options[:key_transform] || :underscore
|
|
210
|
+
CaseTransform.send(transform, hash)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveModelSerializers
|
|
4
|
+
module Adapter
|
|
5
|
+
class JsonApi < Base
|
|
6
|
+
module Error
|
|
7
|
+
# rubocop:disable Style/AsciiComments
|
|
8
|
+
UnknownSourceTypeError = Class.new(ArgumentError)
|
|
9
|
+
|
|
10
|
+
# Builds a JSON API Errors Object
|
|
11
|
+
# {http://jsonapi.org/format/#errors JSON API Errors}
|
|
12
|
+
#
|
|
13
|
+
# @param [ActiveModel::Serializer::ErrorSerializer] error_serializer
|
|
14
|
+
# @return [Array<Symbol, Array<String>>] i.e. attribute_name, [attribute_errors]
|
|
15
|
+
def self.resource_errors(error_serializer, options)
|
|
16
|
+
error_serializer.as_json.flat_map do |attribute_name, attribute_errors|
|
|
17
|
+
attribute_name = JsonApi.send(:transform_key_casing!, attribute_name,
|
|
18
|
+
options)
|
|
19
|
+
attribute_error_objects(attribute_name, attribute_errors)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# definition:
|
|
24
|
+
# JSON Object
|
|
25
|
+
#
|
|
26
|
+
# properties:
|
|
27
|
+
# ☐ id : String
|
|
28
|
+
# ☐ status : String
|
|
29
|
+
# ☐ code : String
|
|
30
|
+
# ☐ title : String
|
|
31
|
+
# ☑ detail : String
|
|
32
|
+
# ☐ links
|
|
33
|
+
# ☐ meta
|
|
34
|
+
# ☑ error_source
|
|
35
|
+
#
|
|
36
|
+
# description:
|
|
37
|
+
# id : A unique identifier for this particular occurrence of the problem.
|
|
38
|
+
# status : The HTTP status code applicable to this problem, expressed as a string value
|
|
39
|
+
# code : An application-specific error code, expressed as a string value.
|
|
40
|
+
# title : A short, human-readable summary of the problem. It **SHOULD NOT** change from
|
|
41
|
+
# occurrence to occurrence of the problem, except for purposes of localization.
|
|
42
|
+
# detail : A human-readable explanation specific to this occurrence of the problem.
|
|
43
|
+
# structure:
|
|
44
|
+
# {
|
|
45
|
+
# title: 'SystemFailure',
|
|
46
|
+
# detail: 'something went terribly wrong',
|
|
47
|
+
# status: '500'
|
|
48
|
+
# }.merge!(errorSource)
|
|
49
|
+
def self.attribute_error_objects(attribute_name, attribute_errors)
|
|
50
|
+
attribute_errors.map do |attribute_error|
|
|
51
|
+
{
|
|
52
|
+
source: error_source(:pointer, attribute_name),
|
|
53
|
+
detail: attribute_error
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# errorSource
|
|
59
|
+
# description:
|
|
60
|
+
# oneOf
|
|
61
|
+
# ☑ pointer : String
|
|
62
|
+
# ☑ parameter : String
|
|
63
|
+
#
|
|
64
|
+
# description:
|
|
65
|
+
# pointer: A JSON Pointer RFC6901 to the associated entity in the request document e.g. "/data"
|
|
66
|
+
# for a primary data object, or "/data/attributes/title" for a specific attribute.
|
|
67
|
+
# https://tools.ietf.org/html/rfc6901
|
|
68
|
+
#
|
|
69
|
+
# parameter: A string indicating which query parameter caused the error
|
|
70
|
+
# structure:
|
|
71
|
+
# if is_attribute?
|
|
72
|
+
# {
|
|
73
|
+
# pointer: '/data/attributes/red-button'
|
|
74
|
+
# }
|
|
75
|
+
# else
|
|
76
|
+
# {
|
|
77
|
+
# parameter: 'pres'
|
|
78
|
+
# }
|
|
79
|
+
# end
|
|
80
|
+
def self.error_source(source_type, attribute_name)
|
|
81
|
+
case source_type
|
|
82
|
+
when :pointer
|
|
83
|
+
{
|
|
84
|
+
pointer: ActiveModelSerializers::JsonPointer.new(:attribute, attribute_name)
|
|
85
|
+
}
|
|
86
|
+
when :parameter
|
|
87
|
+
{
|
|
88
|
+
parameter: attribute_name
|
|
89
|
+
}
|
|
90
|
+
else
|
|
91
|
+
fail UnknownSourceTypeError, "Unknown source type '#{source_type}' for attribute_name '#{attribute_name}'"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
# rubocop:enable Style/AsciiComments
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveModelSerializers
|
|
4
|
+
module Adapter
|
|
5
|
+
class JsonApi < Base
|
|
6
|
+
# {http://jsonapi.org/format/#document-jsonapi-object Jsonapi Object}
|
|
7
|
+
|
|
8
|
+
# toplevel_jsonapi
|
|
9
|
+
# definition:
|
|
10
|
+
# JSON Object
|
|
11
|
+
#
|
|
12
|
+
# properties:
|
|
13
|
+
# version : String
|
|
14
|
+
# meta
|
|
15
|
+
#
|
|
16
|
+
# description:
|
|
17
|
+
# An object describing the server's implementation
|
|
18
|
+
# structure:
|
|
19
|
+
# {
|
|
20
|
+
# version: ActiveModelSerializers.config.jsonapi_version,
|
|
21
|
+
# meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
|
|
22
|
+
# }.reject! { |_, v| v.blank? }
|
|
23
|
+
# prs:
|
|
24
|
+
# https://github.com/rails-api/active_model_serializers/pull/1050
|
|
25
|
+
module Jsonapi
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
def add!(hash)
|
|
29
|
+
hash.merge!(object) if include_object?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def include_object?
|
|
33
|
+
ActiveModelSerializers.config.jsonapi_include_toplevel_object
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# TODO: see if we can cache this
|
|
37
|
+
def object
|
|
38
|
+
object = {
|
|
39
|
+
jsonapi: {
|
|
40
|
+
version: ActiveModelSerializers.config.jsonapi_version,
|
|
41
|
+
meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
object[:jsonapi].reject! { |_, v| v.blank? }
|
|
45
|
+
|
|
46
|
+
object
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveModelSerializers
|
|
4
|
+
module Adapter
|
|
5
|
+
class JsonApi
|
|
6
|
+
# link
|
|
7
|
+
# definition:
|
|
8
|
+
# oneOf
|
|
9
|
+
# linkString
|
|
10
|
+
# linkObject
|
|
11
|
+
#
|
|
12
|
+
# description:
|
|
13
|
+
# A link **MUST** be represented as either: a string containing the link's URL or a link
|
|
14
|
+
# object."
|
|
15
|
+
# structure:
|
|
16
|
+
# if href?
|
|
17
|
+
# linkString
|
|
18
|
+
# else
|
|
19
|
+
# linkObject
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# linkString
|
|
23
|
+
# definition:
|
|
24
|
+
# URI
|
|
25
|
+
#
|
|
26
|
+
# description:
|
|
27
|
+
# A string containing the link's URL.
|
|
28
|
+
# structure:
|
|
29
|
+
# 'http://example.com/link-string'
|
|
30
|
+
#
|
|
31
|
+
# linkObject
|
|
32
|
+
# definition:
|
|
33
|
+
# JSON Object
|
|
34
|
+
#
|
|
35
|
+
# properties:
|
|
36
|
+
# href (required) : URI
|
|
37
|
+
# meta
|
|
38
|
+
# structure:
|
|
39
|
+
# {
|
|
40
|
+
# href: 'http://example.com/link-object',
|
|
41
|
+
# meta: meta,
|
|
42
|
+
# }.reject! {|_,v| v.nil? }
|
|
43
|
+
class Link
|
|
44
|
+
include SerializationContext::UrlHelpers
|
|
45
|
+
|
|
46
|
+
def initialize(serializer, value)
|
|
47
|
+
@_routes ||= nil # handles warning
|
|
48
|
+
# actionpack-4.0.13/lib/action_dispatch/routing/route_set.rb:417: warning: instance variable @_routes not initialized
|
|
49
|
+
@object = serializer.object
|
|
50
|
+
@scope = serializer.scope
|
|
51
|
+
# Use the return value of the block unless it is nil.
|
|
52
|
+
if value.respond_to?(:call)
|
|
53
|
+
@value = instance_eval(&value)
|
|
54
|
+
else
|
|
55
|
+
@value = value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def href(value)
|
|
60
|
+
@href = value
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def meta(value)
|
|
65
|
+
@meta = value
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def as_json
|
|
70
|
+
return @value if @value
|
|
71
|
+
|
|
72
|
+
hash = {}
|
|
73
|
+
hash[:href] = @href if defined?(@href)
|
|
74
|
+
hash[:meta] = @meta if defined?(@meta)
|
|
75
|
+
|
|
76
|
+
hash.any? ? hash : nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
protected
|
|
80
|
+
|
|
81
|
+
attr_reader :object, :scope
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|