jsonapi_parameters 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +207 -0
- data/Rakefile +2 -9
- data/lib/jsonapi_parameters.rb +2 -0
- data/lib/jsonapi_parameters/core_ext.rb +2 -2
- data/lib/jsonapi_parameters/translator.rb +31 -9
- data/lib/jsonapi_parameters/version.rb +4 -2
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02a88091cd1b68f0f385e97308b9b5ed213fe640f5f4831d0e83227cbb24fc98
|
4
|
+
data.tar.gz: 56dc70b94dac08359d21703245e1f5a8b7864bf9a0ec4b487b68057121fcac60
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2ec520d45378a5c44c59f6eb37fd2d809e200eb0203d200d3b388a94dbfacfe0dfab9c1578f5ea446aba52de63deeb618e7baf9263c38573ac406cdedadf9bf
|
7
|
+
data.tar.gz: e9a193c2d58b04be662ae0481d9dc1ff451dbd49b44f7d4b18f0698b99c1958e843c66cb8659bf304db8ed649ac996643738dc6041f466ebc15492f2b330d5cf
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# JsonApi::Parameters
|
2
2
|
Simple JSON:API compliant parameters translator.
|
3
3
|
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/jsonapi_parameters.svg)](https://badge.fury.io/rb/jsonapi_parameters)
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/84fd5b548eea8d7e18af/maintainability)](https://codeclimate.com/github/visualitypl/jsonapi_parameters/maintainability)
|
6
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/84fd5b548eea8d7e18af/test_coverage)](https://codeclimate.com/github/visualitypl/jsonapi_parameters/test_coverage)
|
7
|
+
|
4
8
|
#### The problem
|
5
9
|
|
6
10
|
JSON:API standard specifies not only responses (that can be handled nicely, using gems like [fast_jsonapi from Netflix](https://github.com/Netflix/fast_jsonapi)), but also the request structure.
|
@@ -60,6 +64,209 @@ def create_params
|
|
60
64
|
end
|
61
65
|
```
|
62
66
|
|
67
|
+
#### Relationships
|
68
|
+
|
69
|
+
JsonApi::Parameters supports ActiveRecord relationship parameters, including [nested attributes](https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html).
|
70
|
+
|
71
|
+
Relationship parameters are being read from two optional trees:
|
72
|
+
|
73
|
+
* `relationships`,
|
74
|
+
* `included`
|
75
|
+
|
76
|
+
If you provide any related resources in the `relationships` table, this gem will also look for corresponding, `included` resources and their attributes. Thanks to that this gem supports nested attributes, and will try to translate these included resources and pass them along.
|
77
|
+
|
78
|
+
##### belongs_to
|
79
|
+
|
80
|
+
Passing a resource that is a single entity in relationships tree will make JsonApi::Parameters assume that it is a `belongs_to` relationship.
|
81
|
+
|
82
|
+
###### Without included entity
|
83
|
+
Example:
|
84
|
+
|
85
|
+
```
|
86
|
+
class Movie < ActiveRecord::Model
|
87
|
+
belongs_to :director
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Request body:
|
92
|
+
|
93
|
+
```
|
94
|
+
{
|
95
|
+
data: {
|
96
|
+
type: 'movies',
|
97
|
+
attributes: {
|
98
|
+
title: 'The Terminator',
|
99
|
+
},
|
100
|
+
relationships: {
|
101
|
+
director: {
|
102
|
+
data: {
|
103
|
+
id: 682, type: 'directors'
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
```
|
110
|
+
|
111
|
+
Will translate to:
|
112
|
+
|
113
|
+
```
|
114
|
+
{
|
115
|
+
movie: {
|
116
|
+
title: 'The Terminator',
|
117
|
+
director_id: 682
|
118
|
+
}
|
119
|
+
}
|
120
|
+
```
|
121
|
+
|
122
|
+
|
123
|
+
###### With included entity:
|
124
|
+
Example:
|
125
|
+
|
126
|
+
```
|
127
|
+
class Movie < ActiveRecord::Model
|
128
|
+
belongs_to :director
|
129
|
+
|
130
|
+
accepts_nested_attributes_for :director
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Request body:
|
135
|
+
```
|
136
|
+
{
|
137
|
+
data: {
|
138
|
+
type: 'movies',
|
139
|
+
attributes: {
|
140
|
+
title: 'The Terminator',
|
141
|
+
},
|
142
|
+
relationships: {
|
143
|
+
director: {
|
144
|
+
data: {
|
145
|
+
id: 682, type: 'directors'
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
},
|
150
|
+
included: [
|
151
|
+
{
|
152
|
+
type: 'directors',
|
153
|
+
id: 682,
|
154
|
+
attributes: {
|
155
|
+
name: 'Some guy'
|
156
|
+
}
|
157
|
+
}
|
158
|
+
]
|
159
|
+
}
|
160
|
+
```
|
161
|
+
|
162
|
+
Will translate to:
|
163
|
+
```
|
164
|
+
{
|
165
|
+
movie: {
|
166
|
+
title: 'The Terminator',
|
167
|
+
director_attributes: { id: 682, name: 'Some guy' }
|
168
|
+
}
|
169
|
+
}
|
170
|
+
```
|
171
|
+
|
172
|
+
##### has_many
|
173
|
+
|
174
|
+
Passing a resource that is a an array of entities in relationships tree will make JsonApi::Parameters assume that it is a `has_many` relationship.
|
175
|
+
|
176
|
+
###### Without included entity
|
177
|
+
Example:
|
178
|
+
|
179
|
+
```
|
180
|
+
class Movie < ActiveRecord::Model
|
181
|
+
has_many :genres
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
Request body:
|
186
|
+
|
187
|
+
```
|
188
|
+
{
|
189
|
+
data: {
|
190
|
+
type: 'movies',
|
191
|
+
attributes: {
|
192
|
+
title: 'The Terminator',
|
193
|
+
},
|
194
|
+
relationships: {
|
195
|
+
genres: [{
|
196
|
+
data: {
|
197
|
+
id: 1, type: 'genres'
|
198
|
+
},
|
199
|
+
data: {
|
200
|
+
id: 2, type: 'genres'
|
201
|
+
},
|
202
|
+
}]
|
203
|
+
}
|
204
|
+
}
|
205
|
+
}
|
206
|
+
```
|
207
|
+
|
208
|
+
Will translate to:
|
209
|
+
|
210
|
+
```
|
211
|
+
{
|
212
|
+
movie: {
|
213
|
+
title: 'The Terminator',
|
214
|
+
genre_ids: [1, 2]
|
215
|
+
}
|
216
|
+
}
|
217
|
+
```
|
218
|
+
|
219
|
+
|
220
|
+
###### With included entity:
|
221
|
+
Example:
|
222
|
+
|
223
|
+
```
|
224
|
+
class Movie < ActiveRecord::Model
|
225
|
+
has_many :genres
|
226
|
+
|
227
|
+
accepts_nested_attributes_for :genres
|
228
|
+
end
|
229
|
+
```
|
230
|
+
|
231
|
+
Request body:
|
232
|
+
```
|
233
|
+
{
|
234
|
+
data: {
|
235
|
+
type: 'movies',
|
236
|
+
attributes: {
|
237
|
+
title: 'The Terminator',
|
238
|
+
},
|
239
|
+
relationships: {
|
240
|
+
genres: [{
|
241
|
+
data: {
|
242
|
+
id: 1, type: 'genres'
|
243
|
+
}
|
244
|
+
}]
|
245
|
+
}
|
246
|
+
},
|
247
|
+
included: [
|
248
|
+
{
|
249
|
+
type: 'genre',
|
250
|
+
id: 1,
|
251
|
+
attributes: {
|
252
|
+
name: 'Genre one'
|
253
|
+
}
|
254
|
+
}
|
255
|
+
]
|
256
|
+
}
|
257
|
+
```
|
258
|
+
|
259
|
+
Will translate to:
|
260
|
+
```
|
261
|
+
{
|
262
|
+
movie: {
|
263
|
+
title: 'The Terminator',
|
264
|
+
genres_attributes: [{ id: 1, name: 'Genre one' }]
|
265
|
+
}
|
266
|
+
}
|
267
|
+
```
|
268
|
+
|
269
|
+
|
63
270
|
#### Casing
|
64
271
|
|
65
272
|
If the input is in a different convention than `:snake`, you should specify that.
|
data/Rakefile
CHANGED
@@ -15,13 +15,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
15
15
|
end
|
16
16
|
|
17
17
|
require 'bundler/gem_tasks'
|
18
|
+
require 'rspec/core/rake_task'
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
Rake::TestTask.new(:test) do |t|
|
22
|
-
t.libs << 'test'
|
23
|
-
t.pattern = 'test/**/*_test.rb'
|
24
|
-
t.verbose = false
|
25
|
-
end
|
26
|
-
|
27
|
-
task default: :test
|
20
|
+
task :default => :spec
|
data/lib/jsonapi_parameters.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'jsonapi_parameters/core_ext/action_controller/parameters'
|
2
|
+
require 'jsonapi_parameters/core_ext/action_dispatch/http/mime_type'
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module JsonApi::Parameters
|
2
|
+
include ActiveSupport::Inflector
|
3
|
+
|
2
4
|
def jsonapify(params, naming_convention: :snake)
|
3
5
|
jsonapi_translate(params, naming_convention: naming_convention)
|
4
6
|
end
|
@@ -60,9 +62,9 @@ module JsonApi::Parameters
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def handle_to_many_relation(relationship_key, relationship_value)
|
63
|
-
|
65
|
+
with_inclusion = true
|
64
66
|
|
65
|
-
|
67
|
+
vals = relationship_value.map do |relationship_value|
|
66
68
|
related_id = relationship_value.dig(:id)
|
67
69
|
related_type = relationship_value.dig(:type)
|
68
70
|
|
@@ -70,26 +72,46 @@ module JsonApi::Parameters
|
|
70
72
|
related_id: related_id, related_type: related_type
|
71
73
|
) || {}
|
72
74
|
|
73
|
-
|
75
|
+
# If at least one related object has not been found in `included` tree,
|
76
|
+
# we should not attempt to "#{relationship_key}_attributes" but
|
77
|
+
# "#{relationship_key}_ids" instead.
|
78
|
+
with_inclusion = with_inclusion ? !included_object.empty? : with_inclusion
|
79
|
+
|
80
|
+
if with_inclusion
|
81
|
+
included_object.delete(:type)
|
82
|
+
included_object[:attributes].merge(id: related_id)
|
83
|
+
else
|
84
|
+
relationship_value.dig(:id)
|
85
|
+
end
|
86
|
+
end
|
74
87
|
|
75
|
-
|
88
|
+
# We may have smells in our value array as `with_inclusion` may have been changed at some point
|
89
|
+
# but not in the beginning.
|
90
|
+
# Because of that we should clear it and make sure the results are unified (e.g. array of ids)
|
91
|
+
unless with_inclusion
|
92
|
+
vals.map do |val|
|
93
|
+
val.dig(:attributes, :id) if val.is_a?(Hash)
|
94
|
+
end
|
76
95
|
end
|
77
96
|
|
78
|
-
|
97
|
+
key = with_inclusion ? "#{pluralize(relationship_key)}_attributes".to_sym : "#{singularize(relationship_key)}_ids".to_sym
|
98
|
+
|
99
|
+
[key, vals]
|
79
100
|
end
|
80
101
|
|
81
102
|
def handle_to_one_relation(relationship_key, relationship_value)
|
82
103
|
related_id = relationship_value.dig(:id)
|
83
|
-
related_type =
|
104
|
+
related_type = relationship_value.dig(:type)
|
84
105
|
|
85
106
|
included_object = find_included_object(
|
86
107
|
related_id: related_id, related_type: related_type
|
87
|
-
)
|
108
|
+
) || {}
|
88
109
|
|
89
|
-
return ["#{
|
110
|
+
return ["#{singularize(relationship_key)}_id".to_sym, related_id] if included_object.empty?
|
90
111
|
|
91
112
|
included_object.delete(:type)
|
92
|
-
[
|
113
|
+
included_object = included_object[:attributes].merge(id: related_id)
|
114
|
+
["#{singularize(relationship_key)}_attributes".to_sym, included_object]
|
93
115
|
end
|
94
116
|
|
95
117
|
def find_included_object(related_id:, related_type:)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi_parameters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Visuality
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-05-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -235,6 +235,34 @@ dependencies:
|
|
235
235
|
- - "~>"
|
236
236
|
- !ruby/object:Gem::Version
|
237
237
|
version: 0.3.8
|
238
|
+
- !ruby/object:Gem::Dependency
|
239
|
+
name: simplecov
|
240
|
+
requirement: !ruby/object:Gem::Requirement
|
241
|
+
requirements:
|
242
|
+
- - "~>"
|
243
|
+
- !ruby/object:Gem::Version
|
244
|
+
version: 0.16.1
|
245
|
+
type: :development
|
246
|
+
prerelease: false
|
247
|
+
version_requirements: !ruby/object:Gem::Requirement
|
248
|
+
requirements:
|
249
|
+
- - "~>"
|
250
|
+
- !ruby/object:Gem::Version
|
251
|
+
version: 0.16.1
|
252
|
+
- !ruby/object:Gem::Dependency
|
253
|
+
name: simplecov-console
|
254
|
+
requirement: !ruby/object:Gem::Requirement
|
255
|
+
requirements:
|
256
|
+
- - "~>"
|
257
|
+
- !ruby/object:Gem::Version
|
258
|
+
version: 0.4.2
|
259
|
+
type: :development
|
260
|
+
prerelease: false
|
261
|
+
version_requirements: !ruby/object:Gem::Requirement
|
262
|
+
requirements:
|
263
|
+
- - "~>"
|
264
|
+
- !ruby/object:Gem::Version
|
265
|
+
version: 0.4.2
|
238
266
|
description: JsonApi::Parameters allows you to easily translate JSON:API compliant
|
239
267
|
parameters to a structure expected by Rails.
|
240
268
|
email:
|