jsonapi-resources 0.0.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 +7 -0
- data/.gitignore +20 -0
- data/Gemfile +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +451 -0
- data/Rakefile +24 -0
- data/jsonapi-resources.gemspec +29 -0
- data/lib/jsonapi-resources.rb +2 -0
- data/lib/jsonapi/active_record_operations_processor.rb +17 -0
- data/lib/jsonapi/association.rb +45 -0
- data/lib/jsonapi/error.rb +17 -0
- data/lib/jsonapi/error_codes.rb +16 -0
- data/lib/jsonapi/exceptions.rb +177 -0
- data/lib/jsonapi/operation.rb +151 -0
- data/lib/jsonapi/operation_result.rb +15 -0
- data/lib/jsonapi/operations_processor.rb +47 -0
- data/lib/jsonapi/request.rb +254 -0
- data/lib/jsonapi/resource.rb +417 -0
- data/lib/jsonapi/resource_controller.rb +169 -0
- data/lib/jsonapi/resource_for.rb +25 -0
- data/lib/jsonapi/resource_serializer.rb +209 -0
- data/lib/jsonapi/resources/version.rb +5 -0
- data/lib/jsonapi/routing_ext.rb +104 -0
- data/test/config/database.yml +5 -0
- data/test/controllers/controller_test.rb +940 -0
- data/test/fixtures/active_record.rb +585 -0
- data/test/helpers/functional_helpers.rb +59 -0
- data/test/helpers/hash_helpers.rb +13 -0
- data/test/helpers/value_matchers.rb +60 -0
- data/test/helpers/value_matchers_test.rb +40 -0
- data/test/integration/requests/request_test.rb +39 -0
- data/test/integration/routes/routes_test.rb +85 -0
- data/test/test_helper.rb +98 -0
- data/test/unit/operation/operations_processor_test.rb +188 -0
- data/test/unit/resource/resource_test.rb +45 -0
- data/test/unit/serializer/serializer_test.rb +429 -0
- metadata +193 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
module Helpers
|
2
|
+
module FunctionalHelpers
|
3
|
+
# from http://jamieonsoftware.com/blog/entry/testing-restful-response-types
|
4
|
+
# def assert_response_is(type, message = '')
|
5
|
+
# case type
|
6
|
+
# when :js
|
7
|
+
# check = [
|
8
|
+
# 'text/javascript'
|
9
|
+
# ]
|
10
|
+
# when :json
|
11
|
+
# check = [
|
12
|
+
# 'application/json',
|
13
|
+
# 'text/json',
|
14
|
+
# 'application/x-javascript',
|
15
|
+
# 'text/x-javascript',
|
16
|
+
# 'text/x-json'
|
17
|
+
# ]
|
18
|
+
# when :xml
|
19
|
+
# check = [ 'application/xml', 'text/xml' ]
|
20
|
+
# when :yaml
|
21
|
+
# check = [
|
22
|
+
# 'text/yaml',
|
23
|
+
# 'text/x-yaml',
|
24
|
+
# 'application/yaml',
|
25
|
+
# 'application/x-yaml'
|
26
|
+
# ]
|
27
|
+
# else
|
28
|
+
# if methods.include?('assert_response_types')
|
29
|
+
# check = assert_response_types
|
30
|
+
# else
|
31
|
+
# check = []
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# if @response.content_type
|
36
|
+
# ct = @response.content_type
|
37
|
+
# elsif methods.include?('assert_response_response')
|
38
|
+
# ct = assert_response_response
|
39
|
+
# else
|
40
|
+
# ct = ''
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# begin
|
44
|
+
# assert check.include?(ct)
|
45
|
+
# rescue Test::Unit::AssertionFailedError
|
46
|
+
# raise Test::Unit::AssertionFailedError.new(build_message(message, "The response type is not ?", type.to_s))
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
|
50
|
+
# def assert_js_redirect_to(path)
|
51
|
+
# assert_response_is :js
|
52
|
+
# assert_match /#{"window.location.href = \"" + path + "\""}/, @response.body
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
def json_response
|
56
|
+
JSON.parse(@response.body)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Helpers
|
2
|
+
module HashHelpers
|
3
|
+
def assert_hash_contains(exp, act, msg = nil)
|
4
|
+
msg = message(msg, '') { diff exp, act }
|
5
|
+
assert(matches_hash?(exp, act), msg)
|
6
|
+
end
|
7
|
+
|
8
|
+
def assert_hash_equals(exp, act, msg = nil)
|
9
|
+
msg = message(msg, '') { diff exp, act }
|
10
|
+
assert(matches_hash?(exp, act, {exact: true}), msg)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Helpers
|
2
|
+
module ValueMatchers
|
3
|
+
### Matchers
|
4
|
+
def matches_value?(v1, v2, options = {})
|
5
|
+
if v1 == :any
|
6
|
+
# any value is acceptable
|
7
|
+
elsif v1 == :not_nil
|
8
|
+
return false if v2 == nil
|
9
|
+
elsif v1.kind_of?(Hash)
|
10
|
+
return false unless matches_hash?(v1, v2, options)
|
11
|
+
elsif v1.kind_of?(Array)
|
12
|
+
return false unless matches_array?(v1, v2, options)
|
13
|
+
else
|
14
|
+
return false unless v2 == v1
|
15
|
+
end
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def matches_array?(array1, array2, options = {})
|
20
|
+
return false unless array1.kind_of?(Array) && array2.kind_of?(Array)
|
21
|
+
if options[:exact]
|
22
|
+
return false unless array1.size == array2.size
|
23
|
+
end
|
24
|
+
|
25
|
+
# order of items shouldn't matter:
|
26
|
+
# ['a', 'b', 'c'], ['b', 'c', 'a'] -> true
|
27
|
+
#
|
28
|
+
# matched items should only be used once:
|
29
|
+
# ['a', 'b', 'c'], ['a', 'a', 'a'] -> false
|
30
|
+
# ['a', 'a', 'a'], ['a', 'b', 'c'] -> false
|
31
|
+
matched = {}
|
32
|
+
(0..(array1.size - 1)).each do |i|
|
33
|
+
(0..(array2.size - 1)).each do |j|
|
34
|
+
if !matched.has_value?(j.to_s) && matches_value?(array1[i], array2[j], options)
|
35
|
+
matched[i.to_s] = j.to_s
|
36
|
+
break
|
37
|
+
end
|
38
|
+
end
|
39
|
+
return false unless matched.has_key?(i.to_s)
|
40
|
+
end
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
# options => {exact: true} # hashes must match exactly (i.e. have same number of key-value pairs that are all equal)
|
45
|
+
def matches_hash?(hash1, hash2, options = {})
|
46
|
+
return false unless hash1.kind_of?(Hash) && hash2.kind_of?(Hash)
|
47
|
+
if options[:exact]
|
48
|
+
return false unless hash1.size == hash2.size
|
49
|
+
end
|
50
|
+
|
51
|
+
hash1 = hash1.deep_symbolize_keys
|
52
|
+
hash2 = hash2.deep_symbolize_keys
|
53
|
+
|
54
|
+
hash1.each do |k1, v1|
|
55
|
+
return false unless hash2.has_key?(k1) && matches_value?(v1, hash2[k1], options)
|
56
|
+
end
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class ValueMatchersTest < ActionController::TestCase
|
4
|
+
|
5
|
+
def test_matches_value_any
|
6
|
+
assert(matches_value?(:any, 'a'))
|
7
|
+
assert(matches_value?(:any, nil))
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_matches_value_not_nil
|
11
|
+
assert(matches_value?(:not_nil, 'a'))
|
12
|
+
refute(matches_value?(:not_nil, nil))
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_matches_value_array
|
16
|
+
assert(matches_value?(['a', 'b', 'c'], ['b', 'c', 'a']))
|
17
|
+
assert(matches_value?(['a', 'b', 'c'], ['a', 'b', 'c']))
|
18
|
+
refute(matches_value?(['a', 'b', 'c'], ['a', 'a']))
|
19
|
+
refute(matches_value?(['a', 'b', 'c'], ['a', 'b', 'd']))
|
20
|
+
|
21
|
+
assert(matches_value?(['a', 'b', :any], ['a', 'b', 'c']))
|
22
|
+
assert(matches_value?(['a', 'b', :not_nil], ['a', 'b', 'c']))
|
23
|
+
refute(matches_value?(['a', 'b', :not_nil], ['a', 'b', nil]))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_matches_value_hash
|
27
|
+
assert(matches_value?({a: 'a', b: 'b', c: 'c'}, {a: 'a', b: 'b', c: 'c'}))
|
28
|
+
assert(matches_value?({a: 'a', b: 'b', c: 'c'}, {b: 'b', c: 'c', a: 'a'}))
|
29
|
+
refute(matches_value?({a: 'a', b: 'b', c: 'c'}, {b: 'a', c: 'c', a: 'b'}))
|
30
|
+
|
31
|
+
assert(matches_value?({a: 'a', b: 'b', c: {a: 'a', d: 'e'}}, {b: 'b', c: {a: 'a', d: 'e'}, a: 'a'}))
|
32
|
+
refute(matches_value?({a: 'a', b: 'b', c: {a: 'a', d: 'd'}}, {b: 'b', c: {a: 'a', d: 'e'}, a: 'a'}))
|
33
|
+
|
34
|
+
assert(matches_value?({a: 'a', b: 'b', c: {a: 'a', d: {a: :not_nil}}}, {b: 'b', c: {a: 'a', d: {a: 'b'}}, a: 'a'}))
|
35
|
+
refute(matches_value?({a: 'a', b: 'b', c: {a: 'a', d: {a: :not_nil}}}, {b: 'b', c: {a: 'a', d: {a: nil}}, a: 'a'}))
|
36
|
+
|
37
|
+
assert(matches_value?({a: 'a', b: 'b', c: {a: 'a', d: {a: :any}}}, {b: 'b', c: {a: 'a', d: {a: 'b'}}, a: 'a'}))
|
38
|
+
assert(matches_value?({a: 'a', b: 'b', c: {a: 'a', d: {a: :any}}}, {b: 'b', c: {a: 'a', d: {a: nil}}, a: 'a'}))
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require File.expand_path('../../../fixtures/active_record', __FILE__)
|
3
|
+
|
4
|
+
class RequestTest < ActionDispatch::IntegrationTest
|
5
|
+
|
6
|
+
def test_get
|
7
|
+
get '/posts'
|
8
|
+
assert_equal 200, status
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_put_single
|
12
|
+
put '/posts/3', {"posts" => {"id" => "3", "title" => "A great new Post", "links" => { "tags" => [3,4] }}}
|
13
|
+
assert_equal 200, status
|
14
|
+
end
|
15
|
+
|
16
|
+
# def test_put_links
|
17
|
+
# put '/posts/3/links/tags', {"tags" => [1,4] }
|
18
|
+
# assert_equal 200, status
|
19
|
+
# end
|
20
|
+
|
21
|
+
# def test_patch_create
|
22
|
+
# patch '/posts',
|
23
|
+
# {"op" => "add",
|
24
|
+
# "path" => "/-",
|
25
|
+
# "value" => {"title" => "Another great new Post", "body" => "saasd", "links" => { "author" => 3 }}}
|
26
|
+
# assert_equal 200, status
|
27
|
+
# end
|
28
|
+
|
29
|
+
def test_destroy_single
|
30
|
+
delete '/posts/7'
|
31
|
+
assert_equal 204, status
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_destroy_multiple
|
35
|
+
delete '/posts/8,9'
|
36
|
+
assert_equal 204, status
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class RoutesTest < ActionDispatch::IntegrationTest
|
4
|
+
|
5
|
+
def test_routing_post
|
6
|
+
assert_routing({ path: 'posts', method: :post }, { controller: 'posts', action: 'create' })
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_routing_put
|
10
|
+
assert_routing({ path: '/posts/1', method: :put }, { controller: 'posts', action: 'update', id: '1' })
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_routing_posts_show
|
14
|
+
assert_routing({ path: '/posts/1', method: :get }, {action: 'show', controller: 'posts', id: '1'})
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_routing_posts_links_author_show
|
18
|
+
assert_routing({ path: '/posts/1/links/author', method: :get }, { controller: 'posts', action: 'show_association', post_id: '1', association: 'author' })
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_routing_posts_links_author_destroy
|
22
|
+
assert_routing({ path: '/posts/1/links/author', method: :delete }, { controller: 'posts', action: 'destroy_association', post_id: '1', association: 'author' })
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_routing_posts_links_author_create
|
26
|
+
assert_routing({ path: '/posts/1/links/author', method: :post }, { controller: 'posts', action: 'create_association', post_id: '1', association: 'author' })
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_routing_posts_links_tags_show
|
30
|
+
assert_routing({ path: '/posts/1/links/tags', method: :get }, { controller: 'posts', action: 'show_association', post_id: '1', association: 'tags' })
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_routing_posts_links_tags_destroy
|
34
|
+
assert_routing({ path: '/posts/1/links/tags/1,2', method: :delete }, { controller: 'posts', action: 'destroy_association', post_id: '1', keys: '1,2', association: 'tags' })
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_routing_posts_links_tags_update
|
38
|
+
assert_routing({ path: '/posts/1/links/tags', method: :post }, { controller: 'posts', action: 'create_association', post_id: '1', association: 'tags' })
|
39
|
+
end
|
40
|
+
|
41
|
+
# V1
|
42
|
+
def test_routing_v1_posts_show
|
43
|
+
assert_routing({ path: '/api/v1/posts/1', method: :get }, {action: 'show', controller: 'api/v1/posts', id: '1'})
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_routing_v1_posts_delete
|
47
|
+
assert_routing({ path: '/api/v1/posts/1', method: :delete }, {action: 'destroy', controller: 'api/v1/posts', id: '1'})
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_routing_v1_posts_links_author_show
|
51
|
+
assert_routing({ path: '/api/v1/posts/1/links/author', method: :get }, { controller: 'api/v1/posts', action: 'show_association', post_id: '1', association: 'author' })
|
52
|
+
end
|
53
|
+
|
54
|
+
# V2
|
55
|
+
def test_routing_v2_posts_show
|
56
|
+
assert_routing({ path: '/api/v2/authors/1', method: :get }, {action: 'show', controller: 'api/v2/authors', id: '1'})
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_routing_v2_posts_links_author_show
|
60
|
+
assert_routing({ path: '/api/v2/posts/1/links/author', method: :get }, { controller: 'api/v2/posts', action: 'show_association', post_id: '1', association: 'author' })
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_routing_v2_preferences_show
|
64
|
+
assert_routing({ path: '/api/v2/preferences', method: :get }, {action: 'show', controller: 'api/v2/preferences'})
|
65
|
+
end
|
66
|
+
|
67
|
+
# V3
|
68
|
+
def test_routing_v3_posts_show
|
69
|
+
assert_routing({ path: '/api/v3/posts/1', method: :get }, {action: 'show', controller: 'api/v3/posts', id: '1'})
|
70
|
+
end
|
71
|
+
|
72
|
+
# ToDo: Refute routing
|
73
|
+
# def test_routing_v3_posts_delete
|
74
|
+
# assert_routing({ path: '/api/v3/posts/1', method: :delete }, {action: 'destroy', controller: 'api/v3/posts', id: '1'})
|
75
|
+
# end
|
76
|
+
|
77
|
+
# def test_routing_posts_links_author_except_destroy
|
78
|
+
# assert_routing({ path: '/api/v3/posts/1/links/author', method: :delete }, { controller: 'api/v3/posts', action: 'destroy_association', post_id: '1', association: 'author' })
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# def test_routing_posts_links_tags_only_create_show
|
82
|
+
# assert_routing({ path: '/api/v3/posts/1/links/tags/1,2', method: :delete }, { controller: 'api/v3/posts', action: 'destroy_association', post_id: '1', keys: '1,2', association: 'tags' })
|
83
|
+
# end
|
84
|
+
end
|
85
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
# To run tests with coverage
|
4
|
+
# COVERAGE=true rake test
|
5
|
+
if ENV['COVERAGE']
|
6
|
+
SimpleCov.start do
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'minitest/autorun'
|
11
|
+
require 'minitest/spec'
|
12
|
+
require 'rails/all'
|
13
|
+
|
14
|
+
require 'jsonapi/routing_ext'
|
15
|
+
|
16
|
+
require File.expand_path('../helpers/value_matchers', __FILE__)
|
17
|
+
require File.expand_path('../helpers/hash_helpers', __FILE__)
|
18
|
+
require File.expand_path('../helpers/functional_helpers', __FILE__)
|
19
|
+
|
20
|
+
Rails.env = 'test'
|
21
|
+
|
22
|
+
class TestApp < Rails::Application
|
23
|
+
config.eager_load = false
|
24
|
+
config.root = File.dirname(__FILE__)
|
25
|
+
config.session_store :cookie_store, key: 'session'
|
26
|
+
config.secret_key_base = 'secret'
|
27
|
+
|
28
|
+
#Raise errors on unsupported parameters
|
29
|
+
config.action_controller.action_on_unpermitted_parameters = :raise
|
30
|
+
end
|
31
|
+
|
32
|
+
TestApp.initialize!
|
33
|
+
|
34
|
+
require File.expand_path('../fixtures/active_record', __FILE__)
|
35
|
+
|
36
|
+
TestApp.routes.draw do
|
37
|
+
jsonapi_resources :authors
|
38
|
+
jsonapi_resources :people
|
39
|
+
jsonapi_resources :comments
|
40
|
+
jsonapi_resources :tags
|
41
|
+
jsonapi_resources :posts
|
42
|
+
jsonapi_resources :sections
|
43
|
+
jsonapi_resources :currencies
|
44
|
+
jsonapi_resources :expense_entries
|
45
|
+
jsonapi_resources :breeds
|
46
|
+
jsonapi_resources :planets
|
47
|
+
jsonapi_resources :planet_types
|
48
|
+
jsonapi_resources :moons
|
49
|
+
jsonapi_resources :preferences
|
50
|
+
|
51
|
+
|
52
|
+
namespace :api do
|
53
|
+
namespace :v1 do
|
54
|
+
jsonapi_resources :authors
|
55
|
+
jsonapi_resources :people
|
56
|
+
jsonapi_resources :comments
|
57
|
+
jsonapi_resources :tags
|
58
|
+
jsonapi_resources :posts
|
59
|
+
jsonapi_resources :sections
|
60
|
+
jsonapi_resources :currencies
|
61
|
+
jsonapi_resources :expense_entries
|
62
|
+
jsonapi_resources :breeds
|
63
|
+
jsonapi_resources :planets
|
64
|
+
jsonapi_resources :planet_types
|
65
|
+
jsonapi_resources :moons
|
66
|
+
jsonapi_resources :preferences
|
67
|
+
end
|
68
|
+
|
69
|
+
namespace :v2 do
|
70
|
+
jsonapi_resources :authors
|
71
|
+
jsonapi_resources :posts
|
72
|
+
jsonapi_resource :preferences
|
73
|
+
end
|
74
|
+
|
75
|
+
namespace :v3 do
|
76
|
+
jsonapi_resource :preferences do
|
77
|
+
# Intentionally empty block to skip association urls
|
78
|
+
end
|
79
|
+
|
80
|
+
jsonapi_resources :posts, except: [:destroy] do
|
81
|
+
jsonapi_link :author, except: [:destroy]
|
82
|
+
jsonapi_links :tags, only: [:show, :create]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class MiniTest::Unit::TestCase
|
89
|
+
include Helpers::HashHelpers
|
90
|
+
include Helpers::ValueMatchers
|
91
|
+
include Helpers::FunctionalHelpers
|
92
|
+
end
|
93
|
+
|
94
|
+
class ActiveSupport::TestCase
|
95
|
+
setup do
|
96
|
+
@routes = TestApp.routes
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require File.expand_path('../../../fixtures/active_record', __FILE__)
|
3
|
+
|
4
|
+
require 'jsonapi/operation'
|
5
|
+
require 'jsonapi/operation_result'
|
6
|
+
require 'jsonapi/operations_processor'
|
7
|
+
|
8
|
+
class OperationsProcessorTest < MiniTest::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_create_single_resource
|
13
|
+
op = JSONAPI::OperationsProcessor.new()
|
14
|
+
|
15
|
+
count = Planet.count
|
16
|
+
|
17
|
+
operations = [
|
18
|
+
JSONAPI::CreateResourceOperation.new(PlanetResource, {attributes: {'name' => 'earth', 'description' => 'The best planet ever.'}})
|
19
|
+
]
|
20
|
+
|
21
|
+
request = JSONAPI::Request.new
|
22
|
+
request.operations = operations
|
23
|
+
|
24
|
+
results = op.process(request)
|
25
|
+
|
26
|
+
assert_kind_of(Array, results)
|
27
|
+
assert_kind_of(JSONAPI::OperationResult, results[0])
|
28
|
+
assert_equal(:created, results[0].code)
|
29
|
+
assert_equal(results.size, 1)
|
30
|
+
assert_equal(Planet.count, count + 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_create_multiple_resources
|
34
|
+
op = JSONAPI::OperationsProcessor.new()
|
35
|
+
|
36
|
+
count = Planet.count
|
37
|
+
|
38
|
+
operations = [
|
39
|
+
JSONAPI::CreateResourceOperation.new(PlanetResource, {attributes: {'name' => 'earth', 'description' => 'The best planet for life.'}}),
|
40
|
+
JSONAPI::CreateResourceOperation.new(PlanetResource, {attributes: {'name' => 'mars', 'description' => 'The red planet.'}}),
|
41
|
+
JSONAPI::CreateResourceOperation.new(PlanetResource, {attributes: {'name' => 'venus', 'description' => 'A very hot planet.'}})
|
42
|
+
]
|
43
|
+
|
44
|
+
request = JSONAPI::Request.new
|
45
|
+
request.operations = operations
|
46
|
+
|
47
|
+
results = op.process(request)
|
48
|
+
|
49
|
+
assert_kind_of(Array, results)
|
50
|
+
assert_equal(results.size, 3)
|
51
|
+
assert_equal(Planet.count, count + 3)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_add_has_one_association
|
55
|
+
op = JSONAPI::OperationsProcessor.new()
|
56
|
+
|
57
|
+
saturn = Planet.find(1)
|
58
|
+
gas_giant = PlanetType.find(1)
|
59
|
+
planetoid = PlanetType.find(2)
|
60
|
+
assert_equal(saturn.planet_type_id, planetoid.id)
|
61
|
+
|
62
|
+
|
63
|
+
operations = [
|
64
|
+
JSONAPI::ReplaceHasOneAssociationOperation.new(PlanetResource, saturn.id, :planet_type, gas_giant.id)
|
65
|
+
]
|
66
|
+
|
67
|
+
request = JSONAPI::Request.new
|
68
|
+
request.operations = operations
|
69
|
+
|
70
|
+
results = op.process(request)
|
71
|
+
|
72
|
+
assert_kind_of(Array, results)
|
73
|
+
assert_kind_of(JSONAPI::OperationResult, results[0])
|
74
|
+
assert_equal(:created, results[0].code)
|
75
|
+
assert_equal(results[0].resource.object.attributes['planet_type_id'], gas_giant.id)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_add_has_many_association
|
79
|
+
op = JSONAPI::OperationsProcessor.new()
|
80
|
+
|
81
|
+
betax = Planet.find(5)
|
82
|
+
betay = Planet.find(6)
|
83
|
+
betaz = Planet.find(7)
|
84
|
+
gas_giant = PlanetType.find(1)
|
85
|
+
unknown = PlanetType.find(5)
|
86
|
+
assert_equal(betax.planet_type_id, unknown.id)
|
87
|
+
assert_equal(betay.planet_type_id, unknown.id)
|
88
|
+
assert_equal(betaz.planet_type_id, unknown.id)
|
89
|
+
|
90
|
+
operations = [
|
91
|
+
JSONAPI::CreateHasManyAssociationOperation.new(PlanetTypeResource, gas_giant.id, :planets, [betax.id, betay.id, betaz.id])
|
92
|
+
]
|
93
|
+
|
94
|
+
request = JSONAPI::Request.new
|
95
|
+
request.operations = operations
|
96
|
+
|
97
|
+
results = op.process(request)
|
98
|
+
|
99
|
+
betax.reload
|
100
|
+
betay.reload
|
101
|
+
betaz.reload
|
102
|
+
|
103
|
+
assert_equal(betax.planet_type_id, gas_giant.id)
|
104
|
+
assert_equal(betay.planet_type_id, gas_giant.id)
|
105
|
+
assert_equal(betaz.planet_type_id, gas_giant.id)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_replace_attributes
|
109
|
+
op = JSONAPI::OperationsProcessor.new()
|
110
|
+
|
111
|
+
count = Planet.count
|
112
|
+
saturn = Planet.find(1)
|
113
|
+
assert_equal(saturn.name, 'Satern')
|
114
|
+
|
115
|
+
operations = [
|
116
|
+
JSONAPI::ReplaceFieldsOperation.new(PlanetResource, 1, {attributes: {'name' => 'saturn'}}),
|
117
|
+
]
|
118
|
+
|
119
|
+
request = JSONAPI::Request.new
|
120
|
+
request.operations = operations
|
121
|
+
|
122
|
+
results = op.process(request)
|
123
|
+
|
124
|
+
assert_kind_of(Array, results)
|
125
|
+
assert_equal(results.size, 1)
|
126
|
+
|
127
|
+
assert_kind_of(JSONAPI::OperationResult, results[0])
|
128
|
+
assert_equal(:ok, results[0].code)
|
129
|
+
|
130
|
+
saturn = Planet.find(1)
|
131
|
+
|
132
|
+
assert_equal(saturn.name, 'saturn')
|
133
|
+
|
134
|
+
assert_equal(Planet.count, count)
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_remove_resource
|
138
|
+
op = JSONAPI::OperationsProcessor.new
|
139
|
+
|
140
|
+
count = Planet.count
|
141
|
+
pluto = Planet.find(2)
|
142
|
+
assert_equal(pluto.name, 'Pluto')
|
143
|
+
|
144
|
+
operations = [
|
145
|
+
JSONAPI::RemoveResourceOperation.new(PlanetResource, 2),
|
146
|
+
]
|
147
|
+
|
148
|
+
request = JSONAPI::Request.new
|
149
|
+
request.operations = operations
|
150
|
+
|
151
|
+
results = op.process(request)
|
152
|
+
|
153
|
+
assert_kind_of(Array, results)
|
154
|
+
assert_equal(results.size, 1)
|
155
|
+
|
156
|
+
assert_kind_of(JSONAPI::OperationResult, results[0])
|
157
|
+
assert_equal(:no_content, results[0].code)
|
158
|
+
assert_equal(Planet.count, count - 1)
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_rollback_from_error
|
162
|
+
op = JSONAPI::ActiveRecordOperationsProcessor.new
|
163
|
+
|
164
|
+
count = Planet.count
|
165
|
+
|
166
|
+
operations = [
|
167
|
+
JSONAPI::RemoveResourceOperation.new(PlanetResource, 3),
|
168
|
+
JSONAPI::RemoveResourceOperation.new(PlanetResource, 4),
|
169
|
+
JSONAPI::RemoveResourceOperation.new(PlanetResource, 4)
|
170
|
+
]
|
171
|
+
|
172
|
+
request = JSONAPI::Request.new
|
173
|
+
request.operations = operations
|
174
|
+
|
175
|
+
results = op.process(request)
|
176
|
+
|
177
|
+
assert_equal(Planet.count, count)
|
178
|
+
|
179
|
+
assert_kind_of(Array, results)
|
180
|
+
assert_equal(results.size, 3)
|
181
|
+
|
182
|
+
assert_kind_of(JSONAPI::OperationResult, results[0])
|
183
|
+
assert_equal(:no_content, results[0].code)
|
184
|
+
assert_equal(:no_content, results[1].code)
|
185
|
+
assert_equal(404, results[2].code)
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|