simple_jsonapi 1.0.0

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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +131 -0
  4. data/CHANGELOG.md +2 -0
  5. data/Gemfile +5 -0
  6. data/Jenkinsfile +92 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +532 -0
  9. data/Rakefile +10 -0
  10. data/lib/simple_jsonapi.rb +112 -0
  11. data/lib/simple_jsonapi/definition/attribute.rb +45 -0
  12. data/lib/simple_jsonapi/definition/base.rb +50 -0
  13. data/lib/simple_jsonapi/definition/concerns/has_links_object.rb +36 -0
  14. data/lib/simple_jsonapi/definition/concerns/has_meta_object.rb +36 -0
  15. data/lib/simple_jsonapi/definition/error.rb +70 -0
  16. data/lib/simple_jsonapi/definition/error_source.rb +29 -0
  17. data/lib/simple_jsonapi/definition/link.rb +27 -0
  18. data/lib/simple_jsonapi/definition/meta.rb +27 -0
  19. data/lib/simple_jsonapi/definition/relationship.rb +60 -0
  20. data/lib/simple_jsonapi/definition/resource.rb +104 -0
  21. data/lib/simple_jsonapi/error_serializer.rb +76 -0
  22. data/lib/simple_jsonapi/errors/bad_request.rb +11 -0
  23. data/lib/simple_jsonapi/errors/exception_serializer.rb +6 -0
  24. data/lib/simple_jsonapi/errors/wrapped_error.rb +35 -0
  25. data/lib/simple_jsonapi/errors/wrapped_error_serializer.rb +35 -0
  26. data/lib/simple_jsonapi/helpers/exceptions.rb +39 -0
  27. data/lib/simple_jsonapi/helpers/serializer_inferrer.rb +136 -0
  28. data/lib/simple_jsonapi/helpers/serializer_methods.rb +36 -0
  29. data/lib/simple_jsonapi/node/attributes.rb +51 -0
  30. data/lib/simple_jsonapi/node/base.rb +91 -0
  31. data/lib/simple_jsonapi/node/data/collection.rb +25 -0
  32. data/lib/simple_jsonapi/node/data/singular.rb +26 -0
  33. data/lib/simple_jsonapi/node/document/base.rb +62 -0
  34. data/lib/simple_jsonapi/node/document/collection.rb +17 -0
  35. data/lib/simple_jsonapi/node/document/errors.rb +17 -0
  36. data/lib/simple_jsonapi/node/document/singular.rb +17 -0
  37. data/lib/simple_jsonapi/node/error.rb +55 -0
  38. data/lib/simple_jsonapi/node/error_source.rb +40 -0
  39. data/lib/simple_jsonapi/node/errors.rb +28 -0
  40. data/lib/simple_jsonapi/node/included.rb +45 -0
  41. data/lib/simple_jsonapi/node/object_links.rb +40 -0
  42. data/lib/simple_jsonapi/node/object_meta.rb +40 -0
  43. data/lib/simple_jsonapi/node/relationship.rb +79 -0
  44. data/lib/simple_jsonapi/node/relationship_data/base.rb +53 -0
  45. data/lib/simple_jsonapi/node/relationship_data/collection.rb +32 -0
  46. data/lib/simple_jsonapi/node/relationship_data/singular.rb +33 -0
  47. data/lib/simple_jsonapi/node/relationships.rb +60 -0
  48. data/lib/simple_jsonapi/node/resource/base.rb +21 -0
  49. data/lib/simple_jsonapi/node/resource/full.rb +49 -0
  50. data/lib/simple_jsonapi/node/resource/linkage.rb +25 -0
  51. data/lib/simple_jsonapi/parameters/fields_spec.rb +45 -0
  52. data/lib/simple_jsonapi/parameters/include_spec.rb +57 -0
  53. data/lib/simple_jsonapi/parameters/sort_spec.rb +107 -0
  54. data/lib/simple_jsonapi/serializer.rb +89 -0
  55. data/lib/simple_jsonapi/version.rb +3 -0
  56. data/simple_jsonapi.gemspec +29 -0
  57. data/test/errors/bad_request_test.rb +34 -0
  58. data/test/errors/error_serializer_test.rb +229 -0
  59. data/test/errors/exception_serializer_test.rb +25 -0
  60. data/test/errors/wrapped_error_serializer_test.rb +91 -0
  61. data/test/errors/wrapped_error_test.rb +44 -0
  62. data/test/parameters/fields_spec_test.rb +56 -0
  63. data/test/parameters/include_spec_test.rb +58 -0
  64. data/test/parameters/sort_spec_test.rb +65 -0
  65. data/test/resources/attributes_test.rb +109 -0
  66. data/test/resources/extras_test.rb +70 -0
  67. data/test/resources/id_and_type_test.rb +76 -0
  68. data/test/resources/inclusion_test.rb +134 -0
  69. data/test/resources/links_test.rb +63 -0
  70. data/test/resources/meta_test.rb +49 -0
  71. data/test/resources/relationships_test.rb +262 -0
  72. data/test/resources/sorting_test.rb +79 -0
  73. data/test/resources/sparse_fieldset_test.rb +160 -0
  74. data/test/root_objects_test.rb +165 -0
  75. data/test/test_helper.rb +31 -0
  76. 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