simple_jsonapi 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +131 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +5 -0
- data/Jenkinsfile +92 -0
- data/LICENSE.txt +22 -0
- data/README.md +532 -0
- data/Rakefile +10 -0
- data/lib/simple_jsonapi.rb +112 -0
- data/lib/simple_jsonapi/definition/attribute.rb +45 -0
- data/lib/simple_jsonapi/definition/base.rb +50 -0
- data/lib/simple_jsonapi/definition/concerns/has_links_object.rb +36 -0
- data/lib/simple_jsonapi/definition/concerns/has_meta_object.rb +36 -0
- data/lib/simple_jsonapi/definition/error.rb +70 -0
- data/lib/simple_jsonapi/definition/error_source.rb +29 -0
- data/lib/simple_jsonapi/definition/link.rb +27 -0
- data/lib/simple_jsonapi/definition/meta.rb +27 -0
- data/lib/simple_jsonapi/definition/relationship.rb +60 -0
- data/lib/simple_jsonapi/definition/resource.rb +104 -0
- data/lib/simple_jsonapi/error_serializer.rb +76 -0
- data/lib/simple_jsonapi/errors/bad_request.rb +11 -0
- data/lib/simple_jsonapi/errors/exception_serializer.rb +6 -0
- data/lib/simple_jsonapi/errors/wrapped_error.rb +35 -0
- data/lib/simple_jsonapi/errors/wrapped_error_serializer.rb +35 -0
- data/lib/simple_jsonapi/helpers/exceptions.rb +39 -0
- data/lib/simple_jsonapi/helpers/serializer_inferrer.rb +136 -0
- data/lib/simple_jsonapi/helpers/serializer_methods.rb +36 -0
- data/lib/simple_jsonapi/node/attributes.rb +51 -0
- data/lib/simple_jsonapi/node/base.rb +91 -0
- data/lib/simple_jsonapi/node/data/collection.rb +25 -0
- data/lib/simple_jsonapi/node/data/singular.rb +26 -0
- data/lib/simple_jsonapi/node/document/base.rb +62 -0
- data/lib/simple_jsonapi/node/document/collection.rb +17 -0
- data/lib/simple_jsonapi/node/document/errors.rb +17 -0
- data/lib/simple_jsonapi/node/document/singular.rb +17 -0
- data/lib/simple_jsonapi/node/error.rb +55 -0
- data/lib/simple_jsonapi/node/error_source.rb +40 -0
- data/lib/simple_jsonapi/node/errors.rb +28 -0
- data/lib/simple_jsonapi/node/included.rb +45 -0
- data/lib/simple_jsonapi/node/object_links.rb +40 -0
- data/lib/simple_jsonapi/node/object_meta.rb +40 -0
- data/lib/simple_jsonapi/node/relationship.rb +79 -0
- data/lib/simple_jsonapi/node/relationship_data/base.rb +53 -0
- data/lib/simple_jsonapi/node/relationship_data/collection.rb +32 -0
- data/lib/simple_jsonapi/node/relationship_data/singular.rb +33 -0
- data/lib/simple_jsonapi/node/relationships.rb +60 -0
- data/lib/simple_jsonapi/node/resource/base.rb +21 -0
- data/lib/simple_jsonapi/node/resource/full.rb +49 -0
- data/lib/simple_jsonapi/node/resource/linkage.rb +25 -0
- data/lib/simple_jsonapi/parameters/fields_spec.rb +45 -0
- data/lib/simple_jsonapi/parameters/include_spec.rb +57 -0
- data/lib/simple_jsonapi/parameters/sort_spec.rb +107 -0
- data/lib/simple_jsonapi/serializer.rb +89 -0
- data/lib/simple_jsonapi/version.rb +3 -0
- data/simple_jsonapi.gemspec +29 -0
- data/test/errors/bad_request_test.rb +34 -0
- data/test/errors/error_serializer_test.rb +229 -0
- data/test/errors/exception_serializer_test.rb +25 -0
- data/test/errors/wrapped_error_serializer_test.rb +91 -0
- data/test/errors/wrapped_error_test.rb +44 -0
- data/test/parameters/fields_spec_test.rb +56 -0
- data/test/parameters/include_spec_test.rb +58 -0
- data/test/parameters/sort_spec_test.rb +65 -0
- data/test/resources/attributes_test.rb +109 -0
- data/test/resources/extras_test.rb +70 -0
- data/test/resources/id_and_type_test.rb +76 -0
- data/test/resources/inclusion_test.rb +134 -0
- data/test/resources/links_test.rb +63 -0
- data/test/resources/meta_test.rb +49 -0
- data/test/resources/relationships_test.rb +262 -0
- data/test/resources/sorting_test.rb +79 -0
- data/test/resources/sparse_fieldset_test.rb +160 -0
- data/test/root_objects_test.rb +165 -0
- data/test/test_helper.rb +31 -0
- metadata +235 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class IncludeSpecTest < Minitest::Spec
|
4
|
+
describe SimpleJsonapi::Parameters::IncludeSpec do
|
5
|
+
describe "parsing" do
|
6
|
+
it "accepts a comma-delimited list" do
|
7
|
+
spec = SimpleJsonapi::Parameters::IncludeSpec.new("order,product")
|
8
|
+
assert_equal({ order: {}, product: {} }, spec.to_h)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "accepts a list of strings" do
|
12
|
+
spec = SimpleJsonapi::Parameters::IncludeSpec.new("order", "product")
|
13
|
+
assert_equal({ order: {}, product: {} }, spec.to_h)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "accepts an array of strings" do
|
17
|
+
spec = SimpleJsonapi::Parameters::IncludeSpec.new(["order", "product"])
|
18
|
+
assert_equal({ order: {}, product: {} }, spec.to_h)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "nested includes" do
|
23
|
+
let(:nested_spec) do
|
24
|
+
SimpleJsonapi::Parameters::IncludeSpec.new(
|
25
|
+
"rel_1",
|
26
|
+
"rel_1.rel_1_a",
|
27
|
+
"rel_1.rel_1_a.rel_1_a_i",
|
28
|
+
"rel_1.rel_1_a.rel_1_a_ii",
|
29
|
+
"rel_1.rel_1_b",
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "parses nested includes" do
|
34
|
+
expected_value = {
|
35
|
+
rel_1: {
|
36
|
+
rel_1_a: {
|
37
|
+
rel_1_a_i: {},
|
38
|
+
rel_1_a_ii: {},
|
39
|
+
},
|
40
|
+
rel_1_b: {},
|
41
|
+
},
|
42
|
+
}
|
43
|
+
assert_equal(expected_value, nested_spec.to_h)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "extracts a nested include spec" do
|
47
|
+
expected_value = {
|
48
|
+
rel_1_a: {
|
49
|
+
rel_1_a_i: {},
|
50
|
+
rel_1_a_ii: {},
|
51
|
+
},
|
52
|
+
rel_1_b: {},
|
53
|
+
}
|
54
|
+
assert_equal(expected_value, nested_spec[:rel_1].to_h)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SortSpecTest < Minitest::Spec
|
4
|
+
describe SimpleJsonapi::Parameters::SortSpec do
|
5
|
+
it "interprets a field name as ascending" do
|
6
|
+
spec = SimpleJsonapi::Parameters::SortSpec.new("orders" => "customer_name")
|
7
|
+
|
8
|
+
assert_equal [:customer_name], spec[:orders].map(&:field)
|
9
|
+
assert_equal [:asc], spec[:orders].map(&:dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "interprets a leading hyphen as descending" do
|
13
|
+
spec = SimpleJsonapi::Parameters::SortSpec.new("orders" => "-customer_name")
|
14
|
+
|
15
|
+
assert_equal [:customer_name], spec[:orders].map(&:field)
|
16
|
+
assert_equal [:desc], spec[:orders].map(&:dir)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "accepts comma-delimited strings" do
|
20
|
+
spec = SimpleJsonapi::Parameters::SortSpec.new(
|
21
|
+
"orders" => "customer_name,-description",
|
22
|
+
"line_items" => "-order_id,product_id",
|
23
|
+
)
|
24
|
+
|
25
|
+
assert_equal [:customer_name, :description], spec[:orders].map(&:field)
|
26
|
+
assert_equal [:asc, :desc], spec[:orders].map(&:dir)
|
27
|
+
|
28
|
+
assert_equal [:order_id, :product_id], spec[:line_items].map(&:field)
|
29
|
+
assert_equal [:desc, :asc], spec[:line_items].map(&:dir)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "accepts arrays of strings" do
|
33
|
+
spec = SimpleJsonapi::Parameters::SortSpec.new(
|
34
|
+
"orders" => ["customer_name", "-description"],
|
35
|
+
"line_items" => ["-order_id", "product_id"],
|
36
|
+
)
|
37
|
+
|
38
|
+
assert_equal [:customer_name, :description], spec[:orders].map(&:field)
|
39
|
+
assert_equal [:asc, :desc], spec[:orders].map(&:dir)
|
40
|
+
|
41
|
+
assert_equal [:order_id, :product_id], spec[:line_items].map(&:field)
|
42
|
+
assert_equal [:desc, :asc], spec[:line_items].map(&:dir)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe SimpleJsonapi::Parameters::SortFieldSpec do
|
47
|
+
it "interprets a field name as ascending" do
|
48
|
+
field_spec = SimpleJsonapi::Parameters::SortFieldSpec.new("customer_name")
|
49
|
+
|
50
|
+
assert_equal :customer_name, field_spec.field
|
51
|
+
assert_equal :asc, field_spec.dir
|
52
|
+
assert_equal true, field_spec.asc?
|
53
|
+
assert_equal false, field_spec.desc?
|
54
|
+
end
|
55
|
+
|
56
|
+
it "interprets a leading hyphen as descending" do
|
57
|
+
field_spec = SimpleJsonapi::Parameters::SortFieldSpec.new("-customer_name")
|
58
|
+
|
59
|
+
assert_equal :customer_name, field_spec.field
|
60
|
+
assert_equal :desc, field_spec.dir
|
61
|
+
assert_equal false, field_spec.asc?
|
62
|
+
assert_equal true, field_spec.desc?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AttributesTest < Minitest::Spec
|
4
|
+
class Thing < TestModel
|
5
|
+
attr_accessor :name, :number, :timestamp
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Resource attributes" do
|
9
|
+
describe "rendering" do
|
10
|
+
class BasicSerializer < SimpleJsonapi::Serializer
|
11
|
+
attribute :name
|
12
|
+
attribute(:number) { |res| res.number&.to_i }
|
13
|
+
attribute(:date) { |res| res.timestamp&.to_date&.iso8601 }
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:thing) do
|
17
|
+
Thing.new(
|
18
|
+
id: 1,
|
19
|
+
name: "Thing 1",
|
20
|
+
number: 2.718,
|
21
|
+
timestamp: Time.new(2017, 7, 15, 12, 34, 56),
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:serialized) { SimpleJsonapi.render_resource(thing, serializer: BasicSerializer) }
|
26
|
+
let(:attributes) { serialized.dig(:data, :attributes) }
|
27
|
+
|
28
|
+
it "calls the matching method by default" do
|
29
|
+
assert_equal "Thing 1", attributes[:name]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "executes a proc" do
|
33
|
+
assert_equal 2, attributes[:number]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "conditional rendering" do
|
38
|
+
class ConditionalSerializer < SimpleJsonapi::Serializer
|
39
|
+
attribute :name, if: ->(res) { res.name.starts_with?("A") }
|
40
|
+
attribute :number, unless: ->(res) { res.name.starts_with?("Z") }
|
41
|
+
attribute(:date) { |res| res.timestamp&.to_date&.iso8601 }
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:thing_a) { Thing.new(id: 1, name: "Abigail", number: 123) }
|
45
|
+
let(:thing_z) { Thing.new(id: 2, name: "Zachary", number: 456) }
|
46
|
+
|
47
|
+
def attributes_for(thing)
|
48
|
+
SimpleJsonapi.render_resource(thing, serializer: ConditionalSerializer).dig(:data, :attributes)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "renders the attribute when the 'if' is truthy" do
|
52
|
+
assert_equal "Abigail", attributes_for(thing_a)[:name]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "renders the attribute when the 'unless' is falsey" do
|
56
|
+
assert_equal 123, attributes_for(thing_a)[:number]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "omits the attribute when the 'if' is falsey" do
|
60
|
+
refute_includes attributes_for(thing_z).keys, :name
|
61
|
+
end
|
62
|
+
|
63
|
+
it "omits the attribute when the 'unless' is truthy" do
|
64
|
+
refute_includes attributes_for(thing_z).keys, :number
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "validation" do
|
69
|
+
it "cannot be named id" do
|
70
|
+
assert_raises(ArgumentError) do
|
71
|
+
Class.new(SimpleJsonapi::Serializer) { attribute :id }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "cannot be named type" do
|
76
|
+
assert_raises(ArgumentError) do
|
77
|
+
Class.new(SimpleJsonapi::Serializer) { attribute :type }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "documentation" do
|
83
|
+
class DocSerializer < SimpleJsonapi::Serializer
|
84
|
+
attribute :name
|
85
|
+
attribute :number, type: :numeric, description: "a number"
|
86
|
+
attribute :colors, type: :string, array: true
|
87
|
+
end
|
88
|
+
|
89
|
+
it "builds an attribute without documentation" do
|
90
|
+
attr_defn = DocSerializer.definition.attribute_definitions[:name]
|
91
|
+
assert_nil attr_defn.data_type
|
92
|
+
assert_equal false, attr_defn.array
|
93
|
+
assert_nil attr_defn.description
|
94
|
+
end
|
95
|
+
|
96
|
+
it "stores the type and description" do
|
97
|
+
attr_defn = DocSerializer.definition.attribute_definitions[:number]
|
98
|
+
assert_equal :numeric, attr_defn.data_type
|
99
|
+
assert_equal "a number", attr_defn.description
|
100
|
+
end
|
101
|
+
|
102
|
+
it "stores the type and array-ness" do
|
103
|
+
attr_defn = DocSerializer.definition.attribute_definitions[:colors]
|
104
|
+
assert_equal :string, attr_defn.data_type
|
105
|
+
assert_equal true, attr_defn.array
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ExtrasTest < Minitest::Spec
|
4
|
+
class Order < TestModel
|
5
|
+
attr_accessor :order_number, :total_price, :customer
|
6
|
+
end
|
7
|
+
|
8
|
+
class Customer < TestModel
|
9
|
+
attr_accessor :first_name, :last_name
|
10
|
+
end
|
11
|
+
|
12
|
+
class OrderSerializer < SimpleJsonapi::Serializer
|
13
|
+
attribute(:order_number) { |_order| @override_order_number }
|
14
|
+
attribute(:total_price) { |_order| @override_total_price }
|
15
|
+
has_one :customer, if: ->(_order) { @has_a_customer }
|
16
|
+
end
|
17
|
+
|
18
|
+
class CustomerSerializer < SimpleJsonapi::Serializer
|
19
|
+
attribute(:first_name) { |_customer| @override_first_name }
|
20
|
+
attribute(:last_name) { |_customer| @override_last_name }
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:order) { Order.new(id: 1, order_number: "123", total_price: "5.99", customer: customer) }
|
24
|
+
let(:customer) { Customer.new(id: 2, first_name: "Andrei", last_name: "Romanov") }
|
25
|
+
|
26
|
+
describe "Extra contextual data" do
|
27
|
+
describe "on the root resource" do
|
28
|
+
it "runs when no extras are provided" do
|
29
|
+
serialized = SimpleJsonapi.render_resource(order)
|
30
|
+
assert_nil serialized.dig(:data, :attributes, :order_number)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "makes the extras hash available to the attributes" do
|
34
|
+
serialized = SimpleJsonapi.render_resource(
|
35
|
+
order,
|
36
|
+
extras: {
|
37
|
+
override_order_number: "999",
|
38
|
+
override_total_price: 1.00,
|
39
|
+
},
|
40
|
+
)
|
41
|
+
assert_equal "999", serialized.dig(:data, :attributes, :order_number)
|
42
|
+
assert_equal 1.00, serialized.dig(:data, :attributes, :total_price)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "makes the extras hash available to an if predicate" do
|
46
|
+
serialized = SimpleJsonapi.render_resource(order, extras: { has_a_customer: false })
|
47
|
+
assert_nil serialized.dig(:data, :relationships, :customer, :data)
|
48
|
+
|
49
|
+
serialized = SimpleJsonapi.render_resource(order, extras: { has_a_customer: true })
|
50
|
+
assert_equal "2", serialized.dig(:data, :relationships, :customer, :data, :id)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "on a related resource" do
|
55
|
+
it "makes the extras hash available to the attributes" do
|
56
|
+
serialized = SimpleJsonapi.render_resource(
|
57
|
+
order,
|
58
|
+
include: "customer",
|
59
|
+
extras: {
|
60
|
+
has_a_customer: true,
|
61
|
+
override_first_name: "Jane",
|
62
|
+
override_last_name: "Doe",
|
63
|
+
},
|
64
|
+
)
|
65
|
+
assert_equal "Jane", serialized.dig(:included, 0, :attributes, :first_name)
|
66
|
+
assert_equal "Doe", serialized.dig(:included, 0, :attributes, :last_name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class IdAndTypeTest < Minitest::Spec
|
4
|
+
class Thing < TestModel
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:thing) { Thing.new(id: 4) }
|
8
|
+
|
9
|
+
describe "Resource id and type" do
|
10
|
+
describe "default implementation" do
|
11
|
+
class DefaultsSerializer < SimpleJsonapi::Serializer
|
12
|
+
end
|
13
|
+
|
14
|
+
it "calls the id method" do
|
15
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: DefaultsSerializer)
|
16
|
+
assert_equal "4", serialized.dig(:data, :id)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "uses the objects type" do
|
20
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: DefaultsSerializer)
|
21
|
+
assert_equal "things", serialized.dig(:data, :type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "defined with blocks" do
|
26
|
+
class BlocksSerializer < SimpleJsonapi::Serializer
|
27
|
+
id { |thing| "#{thing.id}-the-thing" }
|
28
|
+
type { |thing| thing.class.name }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "calls the block" do
|
32
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: BlocksSerializer)
|
33
|
+
assert_equal "4-the-thing", serialized.dig(:data, :id)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "calls the block" do
|
37
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: BlocksSerializer)
|
38
|
+
assert_equal "IdAndTypeTest::Thing", serialized.dig(:data, :type)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "defined with procs" do
|
43
|
+
class ProcsSerializer < SimpleJsonapi::Serializer
|
44
|
+
id ->(thing) { "#{thing.id}-the-thing" }
|
45
|
+
type ->(thing) { thing.class.name }
|
46
|
+
end
|
47
|
+
|
48
|
+
it "calls the proc" do
|
49
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: ProcsSerializer)
|
50
|
+
assert_equal "4-the-thing", serialized.dig(:data, :id)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "calls the proc" do
|
54
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: ProcsSerializer)
|
55
|
+
assert_equal "IdAndTypeTest::Thing", serialized.dig(:data, :type)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "defined with constant values" do
|
60
|
+
class ValuesSerializer < SimpleJsonapi::Serializer
|
61
|
+
id 42
|
62
|
+
type "values"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "renders the value" do
|
66
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: ValuesSerializer)
|
67
|
+
assert_equal "42", serialized.dig(:data, :id)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "renders the value" do
|
71
|
+
serialized = SimpleJsonapi.render_resource(thing, serializer: ValuesSerializer)
|
72
|
+
assert_equal "values", serialized.dig(:data, :type)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class InclusionTest < Minitest::Spec
|
4
|
+
class Order < TestModel
|
5
|
+
attr_writer :line_items
|
6
|
+
|
7
|
+
def line_items
|
8
|
+
@line_items ||= []
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class LineItem < TestModel
|
13
|
+
attr_accessor :product
|
14
|
+
end
|
15
|
+
|
16
|
+
class Product < TestModel
|
17
|
+
end
|
18
|
+
|
19
|
+
class OrderSerializer < SimpleJsonapi::Serializer
|
20
|
+
has_many :line_items do
|
21
|
+
data(&:line_items)
|
22
|
+
end
|
23
|
+
has_many :products do
|
24
|
+
data { |o| o.line_items.flat_map(&:product).compact.uniq }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class LineItemSerializer < SimpleJsonapi::Serializer
|
29
|
+
has_one :product do
|
30
|
+
data(&:product)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ProductSerializer < SimpleJsonapi::Serializer
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:order) { Order.new(id: 10, line_items: [line_item]) }
|
38
|
+
let(:line_item) { LineItem.new(id: 20, product: product) }
|
39
|
+
let(:product) { Product.new(id: 30) }
|
40
|
+
|
41
|
+
let(:included_line_item) do
|
42
|
+
serialized[:included]&.find { |res| res[:type] == "line_items" }
|
43
|
+
end
|
44
|
+
let(:included_product) do
|
45
|
+
serialized[:included]&.find { |res| res[:type] == "products" }
|
46
|
+
end
|
47
|
+
|
48
|
+
# def meta_for_resource(resource, relationship_name)
|
49
|
+
# resource.dig(:relationships, relationship_name, :data, :meta)
|
50
|
+
# end
|
51
|
+
|
52
|
+
describe "Including related resources" do
|
53
|
+
describe "without an include parameter" do
|
54
|
+
let(:serialized) { SimpleJsonapi.render_resource(order) }
|
55
|
+
|
56
|
+
it "doesn't include any related resources" do
|
57
|
+
refute serialized.key?(:included)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "adds meta information to the root resource" do
|
61
|
+
assert_equal(
|
62
|
+
{ included: false },
|
63
|
+
serialized.dig(:data, :relationships, :line_items, :data, 0, :meta) # has_many
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "with one level of inclusion" do
|
69
|
+
let(:serialized) do
|
70
|
+
SimpleJsonapi.render_resource(order, include: "line_items")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "includes the related resources" do
|
74
|
+
refute_nil included_line_item
|
75
|
+
assert_nil included_product
|
76
|
+
end
|
77
|
+
|
78
|
+
it "adds meta information to the root resource" do
|
79
|
+
assert_equal(
|
80
|
+
{ included: true },
|
81
|
+
serialized.dig(:data, :relationships, :line_items, :data, 0, :meta) # has_many
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "adds meta information to the included resource" do
|
86
|
+
assert_equal(
|
87
|
+
{ included: false },
|
88
|
+
included_line_item.dig(:relationships, :product, :data, :meta) # has_one
|
89
|
+
)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "with two levels of inclusion" do
|
94
|
+
let(:serialized) do
|
95
|
+
SimpleJsonapi.render_resource(order, include: "line_items,line_items.product")
|
96
|
+
end
|
97
|
+
|
98
|
+
it "includes the related resources" do
|
99
|
+
refute_nil included_line_item
|
100
|
+
refute_nil included_product
|
101
|
+
end
|
102
|
+
|
103
|
+
it "adds meta information to the base resource" do
|
104
|
+
assert_equal(
|
105
|
+
{ included: true },
|
106
|
+
serialized.dig(:data, :relationships, :line_items, :data, 0, :meta) # has_many
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "adds meta information to the included resource" do
|
111
|
+
assert_equal(
|
112
|
+
{ included: true },
|
113
|
+
included_line_item.dig(:relationships, :product, :data, :meta) # has_one
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "duplicate resources" do
|
119
|
+
let(:serialized) do
|
120
|
+
SimpleJsonapi.render_resource(order, include: "line_items,line_items.product,products")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "includes the related object only once" do
|
124
|
+
order_to_products_linkage = serialized.dig(:data, :relationships, :products, :data, 0) # has_many
|
125
|
+
line_item_to_product_linkage = included_line_item.dig(:relationships, :product, :data) # has_one
|
126
|
+
|
127
|
+
assert_equal product.id.to_s, order_to_products_linkage[:id]
|
128
|
+
assert_equal product.id.to_s, line_item_to_product_linkage[:id]
|
129
|
+
|
130
|
+
assert_equal(1, serialized[:included].count { |res| res[:type] == "products" })
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|