jsonapi-resources 0.5.0 → 0.5.1
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/README.md +11 -10
- data/lib/jsonapi/active_record_operations_processor.rb +1 -1
- data/lib/jsonapi/exceptions.rb +3 -2
- data/lib/jsonapi/operation.rb +14 -14
- data/lib/jsonapi/operations_processor.rb +2 -4
- data/lib/jsonapi/request.rb +1 -3
- data/lib/jsonapi/resource.rb +15 -3
- data/lib/jsonapi/resources/version.rb +1 -1
- data/test/controllers/controller_test.rb +96 -11
- data/test/fixtures/active_record.rb +58 -37
- data/test/fixtures/categories.yml +35 -0
- data/test/fixtures/craters.yml +9 -0
- data/test/fixtures/moons.yml +6 -0
- data/test/fixtures/planet_types.yml +19 -0
- data/test/fixtures/planets.yml +47 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/resource/resource_test.rb +11 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1860b6f54c5c18361eee885c58d9a44ca799223
|
4
|
+
data.tar.gz: 75520fb96150d1185d444c474f9b803e1efada5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f335fd832a84998b16b4c10fb349c87fba4bfbc5e97166e3c7875c6d07ec46a6fe8453aba8d2297d12ab1a9c22ee28355695e009384a7bb3f2f41fd2ade7d8db
|
7
|
+
data.tar.gz: bf31f588b77dfa2c95d9ddcd6461ef984aa329c2c17641082662156b01a36c69aa6f51fcb2741f84886f0ccdee5c9d2f6108966cfd842db8bcae5509ebb44dd1
|
data/README.md
CHANGED
@@ -102,16 +102,16 @@ class AuthorResource < JSONAPI::Resource
|
|
102
102
|
|
103
103
|
def fetchable_fields
|
104
104
|
if (context.current_user.guest)
|
105
|
-
super
|
105
|
+
super - [:email]
|
106
106
|
else
|
107
|
-
super
|
107
|
+
super
|
108
108
|
end
|
109
109
|
end
|
110
110
|
end
|
111
111
|
```
|
112
112
|
|
113
|
-
Context flows through from the controller and can be used to control the attributes based on the
|
114
|
-
value).
|
113
|
+
Context flows through from the controller to the resource and can be used to control the attributes based on the
|
114
|
+
current user (or other value).
|
115
115
|
|
116
116
|
##### Creatable and Updatable Attributes
|
117
117
|
|
@@ -262,7 +262,7 @@ end
|
|
262
262
|
|
263
263
|
The relationship methods (`relationship`, `has_one`, and `has_many`) support the following options:
|
264
264
|
|
265
|
-
* `class_name` - a string specifying the underlying class for the related resource
|
265
|
+
* `class_name` - a string specifying the underlying class for the related resource. Defaults to the `class_name` property on the underlying model.
|
266
266
|
* `foreign_key` - the method on the resource used to fetch the related resource. Defaults to `<resource_name>_id` for has_one and `<resource_name>_ids` for has_many relationships.
|
267
267
|
* `acts_as_set` - allows the entire set of related records to be replaced in one operation. Defaults to false if not set.
|
268
268
|
* `polymorphic` - set to true to identify relationships that are polymorphic.
|
@@ -270,7 +270,7 @@ The relationship methods (`relationship`, `has_one`, and `has_many`) support the
|
|
270
270
|
|
271
271
|
`to_one` relationships support the additional option:
|
272
272
|
* `foreign_key_on` - defaults to `:self`. To indicate that the foreign key is on the related resource specify `:related`.
|
273
|
-
|
273
|
+
|
274
274
|
Examples:
|
275
275
|
|
276
276
|
```ruby
|
@@ -338,7 +338,7 @@ class ContactResource < JSONAPI::Resource
|
|
338
338
|
end
|
339
339
|
```
|
340
340
|
|
341
|
-
Then a request could pass in a filter for example `http://example.com/contacts?filter[name_last]=Smith` and the system
|
341
|
+
Then a request could pass in a filter for example `http://example.com/contacts?filter[name_last]=Smith` and the system
|
342
342
|
will find all people where the last name exactly matches Smith.
|
343
343
|
|
344
344
|
##### Default Filters
|
@@ -1276,12 +1276,13 @@ JSONAPI.configure do |config|
|
|
1276
1276
|
# processing. If you want to use Rails' `rescue_from` macro to
|
1277
1277
|
# catch this error and render a 403 status code, you should add
|
1278
1278
|
# the `Pundit::NotAuthorizedError` to the `exception_class_whitelist`.
|
1279
|
+
# Subclasses of the whitelisted classes will also be whitelisted.
|
1279
1280
|
config.exception_class_whitelist = []
|
1280
|
-
|
1281
|
+
|
1281
1282
|
# Resource Linkage
|
1282
1283
|
# Controls the serialization of resource linkage for non compound documents
|
1283
|
-
# NOTE:
|
1284
|
-
config.
|
1284
|
+
# NOTE: always_include_to_many_linkage_data is not currently implemented
|
1285
|
+
config.always_include_to_one_linkage_data = false
|
1285
1286
|
end
|
1286
1287
|
```
|
1287
1288
|
|
@@ -29,7 +29,7 @@ class ActiveRecordOperationsProcessor < JSONAPI::OperationsProcessor
|
|
29
29
|
raise e
|
30
30
|
|
31
31
|
rescue => e
|
32
|
-
if JSONAPI.configuration.exception_class_whitelist.
|
32
|
+
if JSONAPI.configuration.exception_class_whitelist.any? { |k| e.class.ancestors.include?(k) }
|
33
33
|
raise e
|
34
34
|
else
|
35
35
|
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
data/lib/jsonapi/exceptions.rb
CHANGED
@@ -323,10 +323,11 @@ module JSONAPI
|
|
323
323
|
end
|
324
324
|
|
325
325
|
def pointer(attr_or_relationship_name)
|
326
|
+
formatted_attr_or_relationship_name = format_key(attr_or_relationship_name)
|
326
327
|
if resource_relationships.include?(attr_or_relationship_name)
|
327
|
-
"/data/relationships/#{
|
328
|
+
"/data/relationships/#{formatted_attr_or_relationship_name}"
|
328
329
|
else
|
329
|
-
"/data/attributes/#{
|
330
|
+
"/data/attributes/#{formatted_attr_or_relationship_name}"
|
330
331
|
end
|
331
332
|
end
|
332
333
|
end
|
data/lib/jsonapi/operation.rb
CHANGED
@@ -17,12 +17,12 @@ module JSONAPI
|
|
17
17
|
attr_reader :filters, :include_directives, :sort_criteria, :paginator
|
18
18
|
|
19
19
|
def initialize(resource_klass, options = {})
|
20
|
+
super(resource_klass, options)
|
20
21
|
@filters = options[:filters]
|
21
22
|
@include_directives = options[:include_directives]
|
22
23
|
@sort_criteria = options.fetch(:sort_criteria, [])
|
23
24
|
@paginator = options[:paginator]
|
24
25
|
@transactional = false
|
25
|
-
super(resource_klass, options)
|
26
26
|
end
|
27
27
|
|
28
28
|
def record_count
|
@@ -70,10 +70,10 @@ module JSONAPI
|
|
70
70
|
attr_reader :id, :include_directives
|
71
71
|
|
72
72
|
def initialize(resource_klass, options = {})
|
73
|
+
super(resource_klass, options)
|
73
74
|
@id = options.fetch(:id)
|
74
75
|
@include_directives = options[:include_directives]
|
75
76
|
@transactional = false
|
76
|
-
super(resource_klass, options)
|
77
77
|
end
|
78
78
|
|
79
79
|
def apply
|
@@ -94,10 +94,10 @@ module JSONAPI
|
|
94
94
|
attr_reader :parent_key, :relationship_type
|
95
95
|
|
96
96
|
def initialize(resource_klass, options = {})
|
97
|
+
super(resource_klass, options)
|
97
98
|
@parent_key = options.fetch(:parent_key)
|
98
99
|
@relationship_type = options.fetch(:relationship_type)
|
99
100
|
@transactional = false
|
100
|
-
super(resource_klass, options)
|
101
101
|
end
|
102
102
|
|
103
103
|
def apply
|
@@ -116,11 +116,11 @@ module JSONAPI
|
|
116
116
|
attr_reader :source_klass, :source_id, :relationship_type
|
117
117
|
|
118
118
|
def initialize(resource_klass, options = {})
|
119
|
+
super(resource_klass, options)
|
119
120
|
@source_klass = options.fetch(:source_klass)
|
120
121
|
@source_id = options.fetch(:source_id)
|
121
122
|
@relationship_type = options.fetch(:relationship_type)
|
122
123
|
@transactional = false
|
123
|
-
super(resource_klass, options)
|
124
124
|
end
|
125
125
|
|
126
126
|
def apply
|
@@ -139,6 +139,7 @@ module JSONAPI
|
|
139
139
|
attr_reader :source_klass, :source_id, :relationship_type, :filters, :sort_criteria, :paginator
|
140
140
|
|
141
141
|
def initialize(resource_klass, options = {})
|
142
|
+
super(resource_klass, options)
|
142
143
|
@source_klass = options.fetch(:source_klass)
|
143
144
|
@source_id = options.fetch(:source_id)
|
144
145
|
@relationship_type = options.fetch(:relationship_type)
|
@@ -146,7 +147,6 @@ module JSONAPI
|
|
146
147
|
@sort_criteria = options[:sort_criteria]
|
147
148
|
@paginator = options[:paginator]
|
148
149
|
@transactional = false
|
149
|
-
super(resource_klass, options)
|
150
150
|
end
|
151
151
|
|
152
152
|
def apply
|
@@ -168,8 +168,8 @@ module JSONAPI
|
|
168
168
|
attr_reader :data
|
169
169
|
|
170
170
|
def initialize(resource_klass, options = {})
|
171
|
-
@data = options.fetch(:data)
|
172
171
|
super(resource_klass, options)
|
172
|
+
@data = options.fetch(:data)
|
173
173
|
end
|
174
174
|
|
175
175
|
def apply
|
@@ -186,8 +186,8 @@ module JSONAPI
|
|
186
186
|
class RemoveResourceOperation < Operation
|
187
187
|
attr_reader :resource_id
|
188
188
|
def initialize(resource_klass, options = {})
|
189
|
-
@resource_id = options.fetch(:resource_id)
|
190
189
|
super(resource_klass, options)
|
190
|
+
@resource_id = options.fetch(:resource_id)
|
191
191
|
end
|
192
192
|
|
193
193
|
def apply
|
@@ -205,9 +205,9 @@ module JSONAPI
|
|
205
205
|
attr_reader :data, :resource_id
|
206
206
|
|
207
207
|
def initialize(resource_klass, options = {})
|
208
|
+
super(resource_klass, options)
|
208
209
|
@resource_id = options.fetch(:resource_id)
|
209
210
|
@data = options.fetch(:data)
|
210
|
-
super(resource_klass, options)
|
211
211
|
end
|
212
212
|
|
213
213
|
def apply
|
@@ -222,10 +222,10 @@ module JSONAPI
|
|
222
222
|
attr_reader :resource_id, :relationship_type, :key_value
|
223
223
|
|
224
224
|
def initialize(resource_klass, options = {})
|
225
|
+
super(resource_klass, options)
|
225
226
|
@resource_id = options.fetch(:resource_id)
|
226
227
|
@key_value = options.fetch(:key_value)
|
227
228
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
228
|
-
super(resource_klass, options)
|
229
229
|
end
|
230
230
|
|
231
231
|
def apply
|
@@ -240,11 +240,11 @@ module JSONAPI
|
|
240
240
|
attr_reader :resource_id, :relationship_type, :key_value, :key_type
|
241
241
|
|
242
242
|
def initialize(resource_klass, options = {})
|
243
|
+
super(resource_klass, options)
|
243
244
|
@resource_id = options.fetch(:resource_id)
|
244
245
|
@key_value = options.fetch(:key_value)
|
245
246
|
@key_type = options.fetch(:key_type)
|
246
247
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
247
|
-
super(resource_klass, options)
|
248
248
|
end
|
249
249
|
|
250
250
|
def apply
|
@@ -259,10 +259,10 @@ module JSONAPI
|
|
259
259
|
attr_reader :resource_id, :relationship_type, :data
|
260
260
|
|
261
261
|
def initialize(resource_klass, options)
|
262
|
+
super(resource_klass, options)
|
262
263
|
@resource_id = options.fetch(:resource_id)
|
263
264
|
@data = options.fetch(:data)
|
264
265
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
265
|
-
super(resource_klass, options)
|
266
266
|
end
|
267
267
|
|
268
268
|
def apply
|
@@ -277,10 +277,10 @@ module JSONAPI
|
|
277
277
|
attr_reader :resource_id, :relationship_type, :data
|
278
278
|
|
279
279
|
def initialize(resource_klass, options)
|
280
|
+
super(resource_klass, options)
|
280
281
|
@resource_id = options.fetch(:resource_id)
|
281
282
|
@data = options.fetch(:data)
|
282
283
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
283
|
-
super(resource_klass, options)
|
284
284
|
end
|
285
285
|
|
286
286
|
def apply
|
@@ -295,10 +295,10 @@ module JSONAPI
|
|
295
295
|
attr_reader :resource_id, :relationship_type, :associated_key
|
296
296
|
|
297
297
|
def initialize(resource_klass, options)
|
298
|
+
super(resource_klass, options)
|
298
299
|
@resource_id = options.fetch(:resource_id)
|
299
300
|
@associated_key = options.fetch(:associated_key)
|
300
301
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
301
|
-
super(resource_klass, options)
|
302
302
|
end
|
303
303
|
|
304
304
|
def apply
|
@@ -313,9 +313,9 @@ module JSONAPI
|
|
313
313
|
attr_reader :resource_id, :relationship_type
|
314
314
|
|
315
315
|
def initialize(resource_klass, options)
|
316
|
+
super(resource_klass, options)
|
316
317
|
@resource_id = options.fetch(:resource_id)
|
317
318
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
318
|
-
super(resource_klass, options)
|
319
319
|
end
|
320
320
|
|
321
321
|
def apply
|
@@ -33,10 +33,8 @@ module JSONAPI
|
|
33
33
|
# Use transactions if more than one operation and if one of the operations can be transactional
|
34
34
|
# Even if transactional transactions won't be used unless the derived OperationsProcessor supports them.
|
35
35
|
@transactional = false
|
36
|
-
|
37
|
-
@
|
38
|
-
@transactional |= operation.transactional
|
39
|
-
end
|
36
|
+
@operations.each do |operation|
|
37
|
+
@transactional |= operation.transactional
|
40
38
|
end
|
41
39
|
|
42
40
|
run_callbacks :operations do
|
data/lib/jsonapi/request.rb
CHANGED
@@ -604,7 +604,7 @@ module JSONAPI
|
|
604
604
|
end
|
605
605
|
|
606
606
|
def parse_remove_operation(params)
|
607
|
-
keys = parse_key_array(params.
|
607
|
+
keys = parse_key_array(params.require(:id))
|
608
608
|
|
609
609
|
keys.each do |key|
|
610
610
|
@operations.push JSONAPI::RemoveResourceOperation.new(
|
@@ -613,8 +613,6 @@ module JSONAPI
|
|
613
613
|
resource_id: key
|
614
614
|
)
|
615
615
|
end
|
616
|
-
rescue ActionController::UnpermittedParameters => e
|
617
|
-
@errors.concat(JSONAPI::Exceptions::ParametersNotAllowed.new(e.params).errors)
|
618
616
|
rescue JSONAPI::Exceptions::Error => e
|
619
617
|
@errors.concat(e.errors)
|
620
618
|
end
|
data/lib/jsonapi/resource.rb
CHANGED
@@ -596,7 +596,7 @@ module JSONAPI
|
|
596
596
|
end
|
597
597
|
|
598
598
|
def _as_parent_key
|
599
|
-
@_as_parent_key ||= "#{_type.to_s.singularize}
|
599
|
+
@_as_parent_key ||= "#{_type.to_s.singularize}_id"
|
600
600
|
end
|
601
601
|
|
602
602
|
def _allowed_filters
|
@@ -621,7 +621,10 @@ module JSONAPI
|
|
621
621
|
end
|
622
622
|
|
623
623
|
def _model_class
|
624
|
-
@model
|
624
|
+
return @model if @model
|
625
|
+
@model = _model_name.to_s.safe_constantize
|
626
|
+
fail NameError, "model could not be found for #{self.name}" if @model.nil?
|
627
|
+
@model
|
625
628
|
end
|
626
629
|
|
627
630
|
def _allowed_filter?(filter)
|
@@ -670,6 +673,15 @@ module JSONAPI
|
|
670
673
|
|
671
674
|
attrs.each do |attr|
|
672
675
|
check_reserved_relationship_name(attr)
|
676
|
+
|
677
|
+
# Initialize from an ActiveRecord model's properties
|
678
|
+
if _model_class < ActiveRecord::Base
|
679
|
+
model_association = _model_class.reflect_on_association(attr)
|
680
|
+
if model_association
|
681
|
+
options[:class_name] ||= model_association.class_name
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
673
685
|
@_relationships[attr] = relationship = klass.new(attr, options)
|
674
686
|
|
675
687
|
associated_records_method_name = case relationship
|
@@ -741,7 +753,7 @@ module JSONAPI
|
|
741
753
|
|
742
754
|
sort_criteria = options.fetch(:sort_criteria, {})
|
743
755
|
unless sort_criteria.nil? || sort_criteria.empty?
|
744
|
-
order_options =
|
756
|
+
order_options = relationship.resource_klass.construct_order_options(sort_criteria)
|
745
757
|
records = resource_klass.apply_sort(records, order_options)
|
746
758
|
end
|
747
759
|
|
@@ -1427,13 +1427,6 @@ class PostsControllerTest < ActionController::TestCase
|
|
1427
1427
|
assert_equal initial_count, Post.count
|
1428
1428
|
end
|
1429
1429
|
|
1430
|
-
def test_delete_extra_param
|
1431
|
-
initial_count = Post.count
|
1432
|
-
delete :destroy, {id: '4', asdfg: 'aaaa'}
|
1433
|
-
assert_response :bad_request
|
1434
|
-
assert_equal initial_count, Post.count
|
1435
|
-
end
|
1436
|
-
|
1437
1430
|
def test_show_to_one_relationship
|
1438
1431
|
get :show_relationship, {post_id: '1', relationship: 'author'}
|
1439
1432
|
assert_response :success
|
@@ -2159,11 +2152,8 @@ class Api::V1::PostsControllerTest < ActionController::TestCase
|
|
2159
2152
|
end
|
2160
2153
|
|
2161
2154
|
class FactsControllerTest < ActionController::TestCase
|
2162
|
-
def setup
|
2163
|
-
JSONAPI.configuration.json_key_format = :camelized_key
|
2164
|
-
end
|
2165
|
-
|
2166
2155
|
def test_type_formatting
|
2156
|
+
JSONAPI.configuration.json_key_format = :camelized_key
|
2167
2157
|
get :show, {id: '1'}
|
2168
2158
|
assert_response :success
|
2169
2159
|
assert json_response['data'].is_a?(Hash)
|
@@ -2177,6 +2167,40 @@ class FactsControllerTest < ActionController::TestCase
|
|
2177
2167
|
assert_equal 'abc', json_response['data']['attributes']['photo']
|
2178
2168
|
assert_equal false, json_response['data']['attributes']['cool']
|
2179
2169
|
end
|
2170
|
+
|
2171
|
+
def test_create_with_invalid_data
|
2172
|
+
JSONAPI.configuration.json_key_format = :dasherized_key
|
2173
|
+
set_content_type_header!
|
2174
|
+
post :create,
|
2175
|
+
{
|
2176
|
+
data: {
|
2177
|
+
type: 'facts',
|
2178
|
+
attributes: {
|
2179
|
+
bio: '',
|
2180
|
+
:"quality-rating" => '',
|
2181
|
+
:"spouse-name" => '',
|
2182
|
+
salary: 100000,
|
2183
|
+
:"date-time-joined" => '',
|
2184
|
+
birthday: '',
|
2185
|
+
bedtime: '',
|
2186
|
+
photo: 'abc',
|
2187
|
+
cool: false
|
2188
|
+
},
|
2189
|
+
relationships: {
|
2190
|
+
}
|
2191
|
+
}
|
2192
|
+
}
|
2193
|
+
|
2194
|
+
assert_response :unprocessable_entity
|
2195
|
+
|
2196
|
+
assert_equal "/data/attributes/spouse-name", json_response['errors'][0]['source']['pointer']
|
2197
|
+
assert_equal "can't be blank", json_response['errors'][0]['detail']
|
2198
|
+
assert_equal "spouse-name - can't be blank", json_response['errors'][0]['title']
|
2199
|
+
|
2200
|
+
assert_equal "/data/attributes/bio", json_response['errors'][1]['source']['pointer']
|
2201
|
+
assert_equal "can't be blank", json_response['errors'][1]['detail']
|
2202
|
+
assert_equal "bio - can't be blank", json_response['errors'][1]['title']
|
2203
|
+
end
|
2180
2204
|
end
|
2181
2205
|
|
2182
2206
|
class Api::V2::BooksControllerTest < ActionController::TestCase
|
@@ -2634,3 +2658,64 @@ class Api::V1::PlanetsControllerTest < ActionController::TestCase
|
|
2634
2658
|
assert_match /Save failed or was cancelled/, json_response['errors'][0]['detail']
|
2635
2659
|
end
|
2636
2660
|
end
|
2661
|
+
|
2662
|
+
class Api::V1::MoonsControllerTest < ActionController::TestCase
|
2663
|
+
def test_get_related_resource
|
2664
|
+
get :get_related_resource, {crater_id: 'S56D', relationship: 'moon', source: "api/v1/craters"}
|
2665
|
+
assert_response :success
|
2666
|
+
assert_hash_equals json_response,
|
2667
|
+
{
|
2668
|
+
data: {
|
2669
|
+
id: "1",
|
2670
|
+
type: "moons",
|
2671
|
+
links: {self: "http://test.host/moons/1"},
|
2672
|
+
attributes: {name: "Titan", description: "Best known of the Saturn moons."},
|
2673
|
+
relationships: {
|
2674
|
+
planet: {links: {self: "http://test.host/moons/1/relationships/planet", related: "http://test.host/moons/1/planet"}},
|
2675
|
+
craters: {links: {self: "http://test.host/moons/1/relationships/craters", related: "http://test.host/moons/1/craters"}}}
|
2676
|
+
}
|
2677
|
+
}
|
2678
|
+
|
2679
|
+
end
|
2680
|
+
end
|
2681
|
+
|
2682
|
+
class Api::V1::CratersControllerTest < ActionController::TestCase
|
2683
|
+
def test_show_single
|
2684
|
+
get :show, {id: 'S56D'}
|
2685
|
+
assert_response :success
|
2686
|
+
assert json_response['data'].is_a?(Hash)
|
2687
|
+
assert_equal 'S56D', json_response['data']['attributes']['code']
|
2688
|
+
assert_equal 'Very large crater', json_response['data']['attributes']['description']
|
2689
|
+
assert_nil json_response['included']
|
2690
|
+
end
|
2691
|
+
|
2692
|
+
def test_get_related_resources
|
2693
|
+
get :get_related_resources, {moon_id: '1', relationship: 'craters', source: "api/v1/moons"}
|
2694
|
+
assert_response :success
|
2695
|
+
assert_hash_equals json_response,
|
2696
|
+
{
|
2697
|
+
data: [
|
2698
|
+
{id:"A4D3",
|
2699
|
+
type:"craters",
|
2700
|
+
links:{self: "http://test.host/craters/A4D3"},
|
2701
|
+
attributes:{code: "A4D3", description: "Small crater"},
|
2702
|
+
relationships:{moon: {links: {self: "http://test.host/craters/A4D3/relationships/moon", related: "http://test.host/craters/A4D3/moon"}}}
|
2703
|
+
},
|
2704
|
+
{id: "S56D",
|
2705
|
+
type: "craters",
|
2706
|
+
links:{self: "http://test.host/craters/S56D"},
|
2707
|
+
attributes:{code: "S56D", description: "Very large crater"},
|
2708
|
+
relationships:{moon: {links: {self: "http://test.host/craters/S56D/relationships/moon", related: "http://test.host/craters/S56D/moon"}}}
|
2709
|
+
}
|
2710
|
+
]
|
2711
|
+
}
|
2712
|
+
end
|
2713
|
+
|
2714
|
+
def test_show_relationship
|
2715
|
+
get :show_relationship, {crater_id: 'S56D', relationship: 'moon'}
|
2716
|
+
|
2717
|
+
assert_response :success
|
2718
|
+
assert_equal "moons", json_response['data']['type']
|
2719
|
+
assert_equal "1", json_response['data']['id']
|
2720
|
+
end
|
2721
|
+
end
|
@@ -92,6 +92,12 @@ ActiveRecord::Schema.define do
|
|
92
92
|
t.integer :planet_id
|
93
93
|
end
|
94
94
|
|
95
|
+
create_table :craters, id: false, force: true do |t|
|
96
|
+
t.string :code
|
97
|
+
t.string :description
|
98
|
+
t.integer :moon_id
|
99
|
+
end
|
100
|
+
|
95
101
|
create_table :preferences, force: true do |t|
|
96
102
|
t.integer :person_id
|
97
103
|
t.boolean :advanced_mode, default: false
|
@@ -248,6 +254,21 @@ end
|
|
248
254
|
class Section < ActiveRecord::Base
|
249
255
|
end
|
250
256
|
|
257
|
+
class HairCut < ActiveRecord::Base
|
258
|
+
end
|
259
|
+
|
260
|
+
class Property < ActiveRecord::Base
|
261
|
+
end
|
262
|
+
|
263
|
+
class Customer < ActiveRecord::Base
|
264
|
+
end
|
265
|
+
|
266
|
+
class BadlyNamedAttributes < ActiveRecord::Base
|
267
|
+
end
|
268
|
+
|
269
|
+
class Cat < ActiveRecord::Base
|
270
|
+
end
|
271
|
+
|
251
272
|
class IsoCurrency < ActiveRecord::Base
|
252
273
|
self.primary_key = :code
|
253
274
|
# has_many :expense_entries, foreign_key: 'currency_code'
|
@@ -281,6 +302,14 @@ end
|
|
281
302
|
|
282
303
|
class Moon < ActiveRecord::Base
|
283
304
|
belongs_to :planet
|
305
|
+
|
306
|
+
has_many :craters
|
307
|
+
end
|
308
|
+
|
309
|
+
class Crater < ActiveRecord::Base
|
310
|
+
self.primary_key = :code
|
311
|
+
|
312
|
+
belongs_to :moon
|
284
313
|
end
|
285
314
|
|
286
315
|
class Preferences < ActiveRecord::Base
|
@@ -289,6 +318,7 @@ class Preferences < ActiveRecord::Base
|
|
289
318
|
end
|
290
319
|
|
291
320
|
class Fact < ActiveRecord::Base
|
321
|
+
validates :spouse_name, :bio, presence: true
|
292
322
|
end
|
293
323
|
|
294
324
|
class Like < ActiveRecord::Base
|
@@ -360,7 +390,7 @@ class BreedData
|
|
360
390
|
end
|
361
391
|
end
|
362
392
|
|
363
|
-
class
|
393
|
+
class Customer < ActiveRecord::Base
|
364
394
|
has_many :purchase_orders
|
365
395
|
end
|
366
396
|
|
@@ -407,13 +437,6 @@ class Product < ActiveRecord::Base
|
|
407
437
|
has_one :picture, as: :imageable
|
408
438
|
end
|
409
439
|
|
410
|
-
### PORO Data - don't do this in a production app
|
411
|
-
$breed_data = BreedData.new
|
412
|
-
$breed_data.add(Breed.new(0, 'persian'))
|
413
|
-
$breed_data.add(Breed.new(1, 'siamese'))
|
414
|
-
$breed_data.add(Breed.new(2, 'sphinx'))
|
415
|
-
$breed_data.add(Breed.new(3, 'to_delete'))
|
416
|
-
|
417
440
|
### OperationsProcessor
|
418
441
|
class CountingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor
|
419
442
|
after_find_operation do
|
@@ -429,7 +452,7 @@ class ErrorRaisingOperationsProcessor < ActiveRecordOperationsProcessor
|
|
429
452
|
def process_operation(operation)
|
430
453
|
mock_operation = Minitest::Mock.new
|
431
454
|
mock_operation.expect(:apply, true) do
|
432
|
-
raise PostsController::
|
455
|
+
raise PostsController::SubSpecialError
|
433
456
|
end
|
434
457
|
super(mock_operation)
|
435
458
|
end
|
@@ -445,6 +468,7 @@ end
|
|
445
468
|
class PostsController < ActionController::Base
|
446
469
|
include JSONAPI::ActsAsResourceController
|
447
470
|
class SpecialError < StandardError; end
|
471
|
+
class SubSpecialError < PostsController::SpecialError; end
|
448
472
|
|
449
473
|
# This is used to test that classes that are whitelisted are reraised by
|
450
474
|
# the operations processor.
|
@@ -523,6 +547,9 @@ module Api
|
|
523
547
|
class MoonsController < JSONAPI::ResourceController
|
524
548
|
end
|
525
549
|
|
550
|
+
class CratersController < JSONAPI::ResourceController
|
551
|
+
end
|
552
|
+
|
526
553
|
class LikesController < JSONAPI::ResourceController
|
527
554
|
end
|
528
555
|
end
|
@@ -860,13 +887,25 @@ class MoonResource < JSONAPI::Resource
|
|
860
887
|
attribute :description
|
861
888
|
|
862
889
|
has_one :planet
|
890
|
+
has_many :craters
|
891
|
+
end
|
892
|
+
|
893
|
+
class CraterResource < JSONAPI::Resource
|
894
|
+
attribute :code
|
895
|
+
attribute :description
|
896
|
+
|
897
|
+
has_one :moon
|
898
|
+
|
899
|
+
def self.verify_key(key, context = nil)
|
900
|
+
key && String(key)
|
901
|
+
end
|
863
902
|
end
|
864
903
|
|
865
904
|
class PreferencesResource < JSONAPI::Resource
|
866
905
|
attribute :advanced_mode
|
867
906
|
|
868
|
-
has_one :author, foreign_key: :person_id
|
869
|
-
has_many :friends
|
907
|
+
has_one :author, foreign_key: :person_id
|
908
|
+
has_many :friends
|
870
909
|
|
871
910
|
def self.find_by_key(key, options = {})
|
872
911
|
new(Preferences.first)
|
@@ -930,7 +969,7 @@ module Api
|
|
930
969
|
attribute :body
|
931
970
|
attribute :subject
|
932
971
|
|
933
|
-
has_one :writer, foreign_key: 'author_id'
|
972
|
+
has_one :writer, foreign_key: 'author_id', class_name: 'Writer'
|
934
973
|
has_one :section
|
935
974
|
has_many :comments, acts_as_set: false
|
936
975
|
|
@@ -951,6 +990,7 @@ module Api
|
|
951
990
|
PlanetResource = PlanetResource.dup
|
952
991
|
PlanetTypeResource = PlanetTypeResource.dup
|
953
992
|
MoonResource = MoonResource.dup
|
993
|
+
CraterResource = CraterResource.dup
|
954
994
|
PreferencesResource = PreferencesResource.dup
|
955
995
|
EmployeeResource = EmployeeResource.dup
|
956
996
|
FriendResource = FriendResource.dup
|
@@ -1214,28 +1254,9 @@ class BadlyNamedAttributesResource < JSONAPI::Resource
|
|
1214
1254
|
end
|
1215
1255
|
warn 'end testing Name Collisions'
|
1216
1256
|
|
1217
|
-
### PORO
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
saturn = Planet.create(name: 'Satern',
|
1225
|
-
description: 'Saturn is the sixth planet from the Sun and the second largest planet in the Solar System, after Jupiter.',
|
1226
|
-
planet_type_id: planetoid.id)
|
1227
|
-
titan = Moon.create(name:'Titan', description: 'Best known of the Saturn moons.', planet_id: saturn.id)
|
1228
|
-
makemake = Planet.create(name: 'Makemake', description: 'A small planetoid in the Kuiperbelt.', planet_type_id: planetoid.id)
|
1229
|
-
uranus = Planet.create(name: 'Uranus', description: 'Insert adolescent jokes here.', planet_type_id: gas_giant.id)
|
1230
|
-
jupiter = Planet.create(name: 'Jupiter', description: 'A gas giant.', planet_type_id: gas_giant.id)
|
1231
|
-
betax = Planet.create(name: 'Beta X', description: 'Newly discovered Planet X', planet_type_id: unknown.id)
|
1232
|
-
betay = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Y', planet_type_id: unknown.id)
|
1233
|
-
betaz = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Z', planet_type_id: unknown.id)
|
1234
|
-
betaw = Planet.create(name: 'Beta W', description: 'Newly discovered Planet W')
|
1235
|
-
Category.create(name: 'Category A', status: 'active')
|
1236
|
-
Category.create(name: 'Category B', status: 'active')
|
1237
|
-
Category.create(name: 'Category C', status: 'active')
|
1238
|
-
Category.create(name: 'Category D', status: 'inactive')
|
1239
|
-
Category.create(name: 'Category E', status: 'inactive')
|
1240
|
-
Category.create(name: 'Category F', status: 'inactive')
|
1241
|
-
Category.create(name: 'Category G', status: 'inactive')
|
1257
|
+
### PORO Data - don't do this in a production app
|
1258
|
+
$breed_data = BreedData.new
|
1259
|
+
$breed_data.add(Breed.new(0, 'persian'))
|
1260
|
+
$breed_data.add(Breed.new(1, 'siamese'))
|
1261
|
+
$breed_data.add(Breed.new(2, 'sphinx'))
|
1262
|
+
$breed_data.add(Breed.new(3, 'to_delete'))
|
@@ -0,0 +1,35 @@
|
|
1
|
+
category_a:
|
2
|
+
id: 1
|
3
|
+
name: Category A
|
4
|
+
status: active
|
5
|
+
|
6
|
+
category_b:
|
7
|
+
id: 2
|
8
|
+
name: Category B
|
9
|
+
status: active
|
10
|
+
|
11
|
+
category_c:
|
12
|
+
id: 3
|
13
|
+
name: Category C
|
14
|
+
status: active
|
15
|
+
|
16
|
+
category_d:
|
17
|
+
id: 4
|
18
|
+
name: Category D
|
19
|
+
status: inactive
|
20
|
+
|
21
|
+
category_e:
|
22
|
+
id: 5
|
23
|
+
name: Category E
|
24
|
+
status: inactive
|
25
|
+
|
26
|
+
category_f:
|
27
|
+
id: 6
|
28
|
+
name: Category F
|
29
|
+
status: inactive
|
30
|
+
|
31
|
+
category_g:
|
32
|
+
id: 7
|
33
|
+
name: Category G
|
34
|
+
status: inactive
|
35
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
saturn:
|
2
|
+
id: 1
|
3
|
+
name: Satern
|
4
|
+
description: Saturn is the sixth planet from the Sun and the second largest planet in the Solar System, after Jupiter.
|
5
|
+
planet_type_id: 2
|
6
|
+
|
7
|
+
makemake:
|
8
|
+
id: 2
|
9
|
+
name: Makemake
|
10
|
+
description: A small planetoid in the Kuiperbelt.
|
11
|
+
planet_type_id: 2
|
12
|
+
|
13
|
+
uranus:
|
14
|
+
id: 3
|
15
|
+
name: Uranus
|
16
|
+
description: Insert adolescent jokes here.
|
17
|
+
planet_type_id: 1
|
18
|
+
|
19
|
+
jupiter:
|
20
|
+
id: 4
|
21
|
+
name: Jupiter
|
22
|
+
description: A gas giant.
|
23
|
+
planet_type_id: 1
|
24
|
+
|
25
|
+
betax:
|
26
|
+
id: 5
|
27
|
+
name: Beta X
|
28
|
+
description: Newly discovered Planet X
|
29
|
+
planet_type_id: 5
|
30
|
+
|
31
|
+
betay:
|
32
|
+
id: 6
|
33
|
+
name: Beta X
|
34
|
+
description: Newly discovered Planet Y
|
35
|
+
planet_type_id: 5
|
36
|
+
|
37
|
+
betaz:
|
38
|
+
id: 7
|
39
|
+
name: Beta X
|
40
|
+
description: Newly discovered Planet Z
|
41
|
+
planet_type_id: 5
|
42
|
+
|
43
|
+
betaw:
|
44
|
+
id: 8
|
45
|
+
name: Beta W
|
46
|
+
description: Newly discovered Planet W
|
47
|
+
planet_type_id:
|
data/test/test_helper.rb
CHANGED
@@ -110,6 +110,7 @@ TestApp.routes.draw do
|
|
110
110
|
jsonapi_resources :planets
|
111
111
|
jsonapi_resources :planet_types
|
112
112
|
jsonapi_resources :moons
|
113
|
+
jsonapi_resources :craters
|
113
114
|
jsonapi_resources :preferences
|
114
115
|
jsonapi_resources :facts
|
115
116
|
jsonapi_resources :categories
|
@@ -130,6 +131,7 @@ TestApp.routes.draw do
|
|
130
131
|
jsonapi_resources :planets
|
131
132
|
jsonapi_resources :planet_types
|
132
133
|
jsonapi_resources :moons
|
134
|
+
jsonapi_resources :craters
|
133
135
|
jsonapi_resources :preferences
|
134
136
|
jsonapi_resources :likes
|
135
137
|
end
|
@@ -224,6 +226,14 @@ class Minitest::Test
|
|
224
226
|
include Helpers::Assertions
|
225
227
|
include Helpers::ValueMatchers
|
226
228
|
include Helpers::FunctionalHelpers
|
229
|
+
include ActiveRecord::TestFixtures
|
230
|
+
|
231
|
+
def run_in_transaction?
|
232
|
+
true
|
233
|
+
end
|
234
|
+
|
235
|
+
self.fixture_path = "#{Rails.root}/fixtures"
|
236
|
+
fixtures :all
|
227
237
|
end
|
228
238
|
|
229
239
|
class ActiveSupport::TestCase
|
@@ -8,6 +8,9 @@ class ArticleResource < JSONAPI::Resource
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
class NoMatchResource < JSONAPI::Resource
|
12
|
+
end
|
13
|
+
|
11
14
|
class CatResource < JSONAPI::Resource
|
12
15
|
attribute :id
|
13
16
|
attribute :name
|
@@ -52,6 +55,14 @@ class ResourceTest < ActiveSupport::TestCase
|
|
52
55
|
assert_equal(PostResource._model_class, Post)
|
53
56
|
end
|
54
57
|
|
58
|
+
def test_nil_model_class
|
59
|
+
error = assert_raises(NameError) { NoMatchResource._model_class }
|
60
|
+
assert_equal(
|
61
|
+
error.message,
|
62
|
+
"model could not be found for NoMatchResource"
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
55
66
|
def test_model_alternate
|
56
67
|
assert_equal(ArticleResource._model_class, Post)
|
57
68
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Gebhardt
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-07-
|
12
|
+
date: 2015-07-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -170,8 +170,10 @@ files:
|
|
170
170
|
- test/fixtures/author_details.yml
|
171
171
|
- test/fixtures/book_comments.yml
|
172
172
|
- test/fixtures/books.yml
|
173
|
+
- test/fixtures/categories.yml
|
173
174
|
- test/fixtures/comments.yml
|
174
175
|
- test/fixtures/comments_tags.yml
|
176
|
+
- test/fixtures/craters.yml
|
175
177
|
- test/fixtures/customers.yml
|
176
178
|
- test/fixtures/documents.yml
|
177
179
|
- test/fixtures/expense_entries.yml
|
@@ -179,10 +181,13 @@ files:
|
|
179
181
|
- test/fixtures/hair_cuts.yml
|
180
182
|
- test/fixtures/iso_currencies.yml
|
181
183
|
- test/fixtures/line_items.yml
|
184
|
+
- test/fixtures/moons.yml
|
182
185
|
- test/fixtures/numeros_telefone.yml
|
183
186
|
- test/fixtures/order_flags.yml
|
184
187
|
- test/fixtures/people.yml
|
185
188
|
- test/fixtures/pictures.yml
|
189
|
+
- test/fixtures/planet_types.yml
|
190
|
+
- test/fixtures/planets.yml
|
186
191
|
- test/fixtures/posts.yml
|
187
192
|
- test/fixtures/posts_tags.yml
|
188
193
|
- test/fixtures/preferences.yml
|
@@ -239,8 +244,10 @@ test_files:
|
|
239
244
|
- test/fixtures/author_details.yml
|
240
245
|
- test/fixtures/book_comments.yml
|
241
246
|
- test/fixtures/books.yml
|
247
|
+
- test/fixtures/categories.yml
|
242
248
|
- test/fixtures/comments.yml
|
243
249
|
- test/fixtures/comments_tags.yml
|
250
|
+
- test/fixtures/craters.yml
|
244
251
|
- test/fixtures/customers.yml
|
245
252
|
- test/fixtures/documents.yml
|
246
253
|
- test/fixtures/expense_entries.yml
|
@@ -248,10 +255,13 @@ test_files:
|
|
248
255
|
- test/fixtures/hair_cuts.yml
|
249
256
|
- test/fixtures/iso_currencies.yml
|
250
257
|
- test/fixtures/line_items.yml
|
258
|
+
- test/fixtures/moons.yml
|
251
259
|
- test/fixtures/numeros_telefone.yml
|
252
260
|
- test/fixtures/order_flags.yml
|
253
261
|
- test/fixtures/people.yml
|
254
262
|
- test/fixtures/pictures.yml
|
263
|
+
- test/fixtures/planet_types.yml
|
264
|
+
- test/fixtures/planets.yml
|
255
265
|
- test/fixtures/posts.yml
|
256
266
|
- test/fixtures/posts_tags.yml
|
257
267
|
- test/fixtures/preferences.yml
|