jsonapi-resources 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|