1
+ require 'jsonapi/deserializable/resource'
1
2
module ActiveModelSerializers
2
3
module Adapter
3
4
class JsonApi
4
- # NOTE(Experimental):
5
- # This is an experimental feature. Both the interface and internals could be subject
6
- # to changes.
7
5
module Deserialization
8
6
InvalidDocument = Class . new ( ArgumentError )
9
7
@@ -72,72 +70,41 @@ module Deserialization
72
70
# # author_type: 'people'
73
71
# # }
74
72
#
73
+ # @example
74
+ # def deserialized_params
75
+ # ActionController::Parameters.new(
76
+ # ActiveModelSerializers::Deserialization.jsonapi_parse!(
77
+ # request.request_parameters # no need for request.query_parameters, request.path_parameters
78
+ # )
79
+ # )
80
+ # end
75
81
def parse! ( document , options = { } )
76
- parse ( document , options ) do |invalid_payload , reason |
77
- fail InvalidDocument , "Invalid payload (#{ reason } ): #{ invalid_payload } "
82
+ document = document . dup . permit! . to_h if document . is_a? ( ActionController ::Parameters )
83
+ primary_data = document . slice ( 'data' )
84
+ hash = JSONAPI ::Deserializable ::Resource . call ( primary_data )
85
+ id_keys = hash . keys . select { |key | key . to_s . end_with? ( '_id' ) } . map { |key | key . to_s . sub ( /_id\z / , '' ) }
86
+ ids_keys = hash . keys . select { |key | key . to_s . end_with? ( '_ids' ) } . map { |key | key . to_s . sub ( /_ids\z / , '' ) }
87
+ excluded_keys = id_keys . map { |key | "#{ key } _type" } . concat ( ids_keys . map { |key | "#{ key } _types" } )
88
+ excluded_keys << :type
89
+ excluded_keys . map! ( &:to_sym )
90
+ hash = hash . except ( *excluded_keys )
91
+ filter_fields ( hash , options )
92
+ transform_keys ( hash , options )
93
+ rescue NoMethodError , JSONAPI ::Parser ::InvalidDocument => e
94
+ if block_given?
95
+ yield ( e )
96
+ else
97
+ raise InvalidDocument , "Invalid payload (#{ e . message } ): #{ primary_data } "
78
98
end
79
99
end
80
100
81
101
# Same as parse!, but returns an empty hash instead of raising InvalidDocument
82
102
# on invalid payloads.
83
103
def parse ( document , options = { } )
84
- document = document . dup . permit! . to_h if document . is_a? ( ActionController ::Parameters )
85
-
86
- validate_payload ( document ) do |invalid_document , reason |
87
- yield invalid_document , reason if block_given?
88
- return { }
89
- end
90
-
91
- primary_data = document [ 'data' ]
92
- attributes = primary_data [ 'attributes' ] || { }
93
- attributes [ 'id' ] = primary_data [ 'id' ] if primary_data [ 'id' ]
94
- relationships = primary_data [ 'relationships' ] || { }
95
-
96
- filter_fields ( attributes , options )
97
- filter_fields ( relationships , options )
98
-
99
- hash = { }
100
- hash . merge! ( parse_attributes ( attributes , options ) )
101
- hash . merge! ( parse_relationships ( relationships , options ) )
102
-
103
- hash
104
- end
105
-
106
- # Checks whether a payload is compliant with the JSON API spec.
107
- #
108
- # @api private
109
- # rubocop:disable Metrics/CyclomaticComplexity
110
- def validate_payload ( payload )
111
- unless payload . is_a? ( Hash )
112
- yield payload , 'Expected hash'
113
- return
114
- end
115
-
116
- primary_data = payload [ 'data' ]
117
- unless primary_data . is_a? ( Hash )
118
- yield payload , { data : 'Expected hash' }
119
- return
120
- end
121
-
122
- attributes = primary_data [ 'attributes' ] || { }
123
- unless attributes . is_a? ( Hash )
124
- yield payload , { data : { attributes : 'Expected hash or nil' } }
125
- return
126
- end
127
-
128
- relationships = primary_data [ 'relationships' ] || { }
129
- unless relationships . is_a? ( Hash )
130
- yield payload , { data : { relationships : 'Expected hash or nil' } }
131
- return
132
- end
133
-
134
- relationships . each do |( key , value ) |
135
- unless value . is_a? ( Hash ) && value . key? ( 'data' )
136
- yield payload , { data : { relationships : { key => 'Expected hash with :data key' } } }
137
- end
104
+ parse! ( document , options ) do
105
+ { }
138
106
end
139
107
end
140
- # rubocop:enable Metrics/CyclomaticComplexity
141
108
142
109
# @api private
143
110
def filter_fields ( fields , options )
@@ -148,60 +115,6 @@ def filter_fields(fields, options)
148
115
end
149
116
end
150
117
151
- # @api private
152
- def field_key ( field , options )
153
- ( options [ :keys ] || { } ) . fetch ( field . to_sym , field ) . to_sym
154
- end
155
-
156
- # @api private
157
- def parse_attributes ( attributes , options )
158
- transform_keys ( attributes , options )
159
- . map { |( k , v ) | { field_key ( k , options ) => v } }
160
- . reduce ( { } , :merge )
161
- end
162
-
163
- # Given an association name, and a relationship data attribute, build a hash
164
- # mapping the corresponding ActiveRecord attribute to the corresponding value.
165
- #
166
- # @example
167
- # parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' },
168
- # { 'id' => '2', 'type' => 'comments' }],
169
- # {})
170
- # # => { :comment_ids => ['1', '2'] }
171
- # parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {})
172
- # # => { :author_id => '1' }
173
- # parse_relationship(:author, nil, {})
174
- # # => { :author_id => nil }
175
- # @param [Symbol] assoc_name
176
- # @param [Hash] assoc_data
177
- # @param [Hash] options
178
- # @return [Hash{Symbol, Object}]
179
- #
180
- # @api private
181
- def parse_relationship ( assoc_name , assoc_data , options )
182
- prefix_key = field_key ( assoc_name , options ) . to_s . singularize
183
- hash =
184
- if assoc_data . is_a? ( Array )
185
- { "#{ prefix_key } _ids" . to_sym => assoc_data . map { |ri | ri [ 'id' ] } }
186
- else
187
- { "#{ prefix_key } _id" . to_sym => assoc_data ? assoc_data [ 'id' ] : nil }
188
- end
189
-
190
- polymorphic = ( options [ :polymorphic ] || [ ] ) . include? ( assoc_name . to_sym )
191
- if polymorphic
192
- hash [ "#{ prefix_key } _type" . to_sym ] = assoc_data . present? ? assoc_data [ 'type' ] : nil
193
- end
194
-
195
- hash
196
- end
197
-
198
- # @api private
199
- def parse_relationships ( relationships , options )
200
- transform_keys ( relationships , options )
201
- . map { |( k , v ) | parse_relationship ( k , v [ 'data' ] , options ) }
202
- . reduce ( { } , :merge )
203
- end
204
-
205
118
# @api private
206
119
def transform_keys ( hash , options )
207
120
transform = options [ :key_transform ] || :underscore
0 commit comments