jat 0.0.1 → 0.0.3

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +21 -0
  4. data/jat.gemspec +37 -0
  5. data/lib/jat/attribute.rb +85 -0
  6. data/lib/jat/config.rb +38 -0
  7. data/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads.rb +29 -0
  8. data/lib/jat/plugins/_activerecord_preloads/lib/preloader.rb +89 -0
  9. data/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord.rb +22 -0
  10. data/lib/jat/plugins/_json_api_activerecord/lib/preloads.rb +84 -0
  11. data/lib/jat/plugins/_preloads/_preloads.rb +53 -0
  12. data/lib/jat/plugins/_preloads/lib/format_user_preloads.rb +52 -0
  13. data/lib/jat/plugins/_preloads/lib/preloads_with_path.rb +78 -0
  14. data/lib/jat/plugins/cache/cache.rb +39 -0
  15. data/lib/jat/plugins/camel_lower/camel_lower.rb +18 -0
  16. data/lib/jat/plugins/json_api/json_api.rb +207 -0
  17. data/lib/jat/plugins/json_api/lib/construct_traversal_map.rb +91 -0
  18. data/lib/jat/plugins/json_api/lib/map.rb +54 -0
  19. data/lib/jat/plugins/json_api/lib/params/fields/parse.rb +27 -0
  20. data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +55 -0
  21. data/lib/jat/plugins/json_api/lib/params/fields.rb +23 -0
  22. data/lib/jat/plugins/json_api/lib/params/include/parse.rb +55 -0
  23. data/lib/jat/plugins/json_api/lib/params/include/validate.rb +29 -0
  24. data/lib/jat/plugins/json_api/lib/params/include.rb +49 -0
  25. data/lib/jat/plugins/json_api/lib/presenters/document_links_presenter.rb +48 -0
  26. data/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter.rb +48 -0
  27. data/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter.rb +48 -0
  28. data/lib/jat/plugins/json_api/lib/presenters/links_presenter.rb +48 -0
  29. data/lib/jat/plugins/json_api/lib/presenters/meta_presenter.rb +48 -0
  30. data/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter.rb +53 -0
  31. data/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter.rb +53 -0
  32. data/lib/jat/plugins/json_api/lib/response.rb +239 -0
  33. data/lib/jat/plugins/json_api/lib/traversal_map.rb +34 -0
  34. data/lib/jat/plugins/simple_api/lib/construct_traversal_map.rb +45 -0
  35. data/lib/jat/plugins/simple_api/lib/map.rb +29 -0
  36. data/lib/jat/plugins/simple_api/lib/params/parse.rb +68 -0
  37. data/lib/jat/plugins/simple_api/lib/response.rb +134 -0
  38. data/lib/jat/plugins/simple_api/simple_api.rb +65 -0
  39. data/lib/jat/plugins/to_str/to_str.rb +44 -0
  40. data/lib/jat/plugins.rb +39 -0
  41. data/lib/jat/presenter.rb +51 -0
  42. data/lib/jat/utils/enum_deep_dup.rb +29 -0
  43. data/lib/jat/utils/enum_deep_freeze.rb +19 -0
  44. data/lib/jat.rb +66 -144
  45. data/test/lib/jat/attribute_test.rb +142 -0
  46. data/test/lib/jat/config_test.rb +57 -0
  47. data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +40 -0
  48. data/test/lib/jat/plugins/_activerecord_preloads/lib/preloader_test.rb +98 -0
  49. data/test/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord_test.rb +29 -0
  50. data/test/lib/jat/plugins/_json_api_activerecord/lib/preloads_test.rb +191 -0
  51. data/test/lib/jat/plugins/_preloads/_preloads_test.rb +68 -0
  52. data/test/lib/jat/plugins/_preloads/lib/format_user_preloads_test.rb +47 -0
  53. data/test/lib/jat/plugins/_preloads/lib/preloads_with_path_test.rb +33 -0
  54. data/test/lib/jat/plugins/cache/cache_test.rb +82 -0
  55. data/test/lib/jat/plugins/camel_lower/camel_lower_test.rb +78 -0
  56. data/test/lib/jat/plugins/json_api/json_api_test.rb +154 -0
  57. data/test/lib/jat/plugins/json_api/lib/construct_traversal_map_test.rb +119 -0
  58. data/test/lib/jat/plugins/json_api/lib/map_test.rb +117 -0
  59. data/test/lib/jat/plugins/json_api/lib/params/fields/parse_test.rb +24 -0
  60. data/test/lib/jat/plugins/json_api/lib/params/fields/validate_test.rb +47 -0
  61. data/test/lib/jat/plugins/json_api/lib/params/fields_test.rb +37 -0
  62. data/test/lib/jat/plugins/json_api/lib/params/include/parse_test.rb +46 -0
  63. data/test/lib/jat/plugins/json_api/lib/params/include/validate_test.rb +51 -0
  64. data/test/lib/jat/plugins/json_api/lib/params/include_test.rb +41 -0
  65. data/test/lib/jat/plugins/json_api/lib/presenters/document_links_presenter_test.rb +69 -0
  66. data/test/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter_test.rb +69 -0
  67. data/test/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter_test.rb +69 -0
  68. data/test/lib/jat/plugins/json_api/lib/presenters/links_presenter_test.rb +69 -0
  69. data/test/lib/jat/plugins/json_api/lib/presenters/meta_presenter_test.rb +69 -0
  70. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter_test.rb +75 -0
  71. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter_test.rb +75 -0
  72. data/test/lib/jat/plugins/json_api/lib/response_test.rb +489 -0
  73. data/test/lib/jat/plugins/json_api/lib/traversal_map_test.rb +58 -0
  74. data/test/lib/jat/plugins/simple_api/lib/construct_traversal_map_test.rb +100 -0
  75. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +56 -0
  76. data/test/lib/jat/plugins/simple_api/lib/params/parse_test.rb +71 -0
  77. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +342 -0
  78. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +81 -0
  79. data/test/lib/jat/plugins/to_str/to_str_test.rb +52 -0
  80. data/test/lib/jat/presenter_test.rb +61 -0
  81. data/test/lib/jat/utils/enum_deep_dup_test.rb +31 -0
  82. data/test/lib/jat/utils/enum_deep_freeze_test.rb +28 -0
  83. data/test/lib/jat_test.rb +120 -0
  84. data/test/lib/plugin_test.rb +49 -0
  85. data/test/support/activerecord.rb +24 -0
  86. data/test/test_helper.rb +16 -0
  87. data/test/test_plugin.rb +59 -0
  88. metadata +240 -11
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::Preloads::PreloadsWithPath" do
6
+ before { Jat::Plugins.load_plugin(:_preloads) }
7
+
8
+ let(:described_class) { Jat::Plugins::Preloads::PreloadsWithPath }
9
+
10
+ it "returns provided preloads and path to last value" do
11
+ preloads = {a: {b: {c: {}}, d: {}}, e: {}}
12
+ new_preloads, path = described_class.call(preloads)
13
+
14
+ assert_equal({a: {b: {c: {}}, d: {}}, e: {}}, new_preloads)
15
+ assert_equal(%i[e], path)
16
+ end
17
+
18
+ it "returns provided preloads and path to marked with `!` value" do
19
+ preloads = {a: {b!: {c: {}}, d: {}}, e: {}}
20
+ new_preloads, path = described_class.call(preloads)
21
+
22
+ assert_equal({a: {b: {c: {}}, d: {}}, e: {}}, new_preloads)
23
+ assert_equal(%i[a b], path)
24
+ end
25
+
26
+ it "returns empty preloads and path when empty hash provided" do
27
+ preloads = {}
28
+ new_preloads, path = described_class.call(preloads)
29
+
30
+ assert_equal({}, new_preloads)
31
+ assert_equal([], path)
32
+ end
33
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::Cache" do
6
+ let(:jat_class) do
7
+ new_class = Class.new(Jat)
8
+ to_h_mod = Module.new do
9
+ def to_h
10
+ "RES_#{object}"
11
+ end
12
+ end
13
+ new_class.include(to_h_mod)
14
+ new_class.plugin(:cache)
15
+ new_class
16
+ end
17
+
18
+ let(:context) { {cache: hash_cache} }
19
+ let(:hash_storage) { {} }
20
+ let(:hash_cache) do
21
+ hash_storage
22
+ lambda do |object, context, &block|
23
+ key = [object, context[:foo], context[:_format]].join(".").freeze
24
+ hash_storage[key] ||= block.call
25
+ end
26
+ end
27
+
28
+ describe "InstanceMethods" do
29
+ describe "#to_h" do
30
+ it "takes result from cache" do
31
+ result1 = jat_class.new("OBJECT", context).to_h
32
+ result2 = jat_class.new("OBJECT", context).to_h
33
+
34
+ assert_equal "RES_OBJECT", result1
35
+ assert_equal "RES_OBJECT", result2
36
+ assert_same result1, result2
37
+ end
38
+
39
+ it "does not take cached result when cache keys are different" do
40
+ result1 = jat_class.new("OBJECT", context).to_h
41
+ result2 = jat_class.new("OBJECT", context.merge(foo: :bazz)).to_h
42
+
43
+ assert_equal "RES_OBJECT", result1
44
+ assert_equal "RES_OBJECT", result2
45
+ refute_same result1, result2
46
+ end
47
+
48
+ it "does not saves cache when no context[:cache] provided" do
49
+ jat_class.new("OBJECT", {}).to_str
50
+
51
+ assert_equal [], hash_storage.keys
52
+ end
53
+ end
54
+
55
+ describe "#to_str" do
56
+ it "takes result from cache" do
57
+ result1 = jat_class.new("OBJECT", context).to_str
58
+ result2 = jat_class.new("OBJECT", context).to_str
59
+
60
+ assert_equal '"RES_OBJECT"', result1
61
+ assert_equal '"RES_OBJECT"', result2
62
+ assert_same result1, result2
63
+ end
64
+
65
+ it "does not take cached result when cache keys are different" do
66
+ result1 = jat_class.new("OBJECT", context).to_str
67
+ result2 = jat_class.new("OBJECT", context.merge(foo: :bazz)).to_str
68
+
69
+ assert_equal '"RES_OBJECT"', result1
70
+ assert_equal '"RES_OBJECT"', result2
71
+ refute_same result1, result2
72
+ end
73
+
74
+ it "does not saves cache for #to_h" do
75
+ context[:foo] = :bar
76
+ jat_class.new("OBJECT", context).to_str
77
+
78
+ assert_equal ["OBJECT.bar.to_str"], hash_storage.keys
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::CamelLower" do
6
+ let(:jat_class) do
7
+ new_class = Class.new(Jat)
8
+ new_class.plugin(:camel_lower)
9
+ new_class
10
+ end
11
+
12
+ describe "Attribute" do
13
+ describe "#name" do
14
+ it "returns name in camel_lower case" do
15
+ attribute = jat_class.attribute(:foo)
16
+ assert :foo, attribute.name
17
+
18
+ attribute = jat_class.attribute(:foo_bar)
19
+ assert :fooBar, attribute.name
20
+
21
+ attribute = jat_class.attribute(:foo_bar_bazz)
22
+ assert :fooBarBazz, attribute.name
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "Test responses with simple_api plugin" do
28
+ before { jat_class.plugin(:simple_api) }
29
+
30
+ it "returns attributes in camelLower case" do
31
+ jat_class.attribute(:foo_bar) { 1 }
32
+
33
+ response = jat_class.to_h(true)
34
+ assert_equal({fooBar: 1}, response)
35
+ end
36
+
37
+ it "accepts fields in camelLower format" do
38
+ jat_class.attribute(:foo_bar, exposed: false) { 1 }
39
+
40
+ response = jat_class.to_h(true, params: {fields: "fooBar"})
41
+ assert_equal({fooBar: 1}, response)
42
+ end
43
+ end
44
+
45
+ describe "Test responses with json_api plugin" do
46
+ before do
47
+ jat_class.plugin(:json_api)
48
+ jat_class.type :foo
49
+ jat_class.attribute(:id) { object }
50
+ end
51
+
52
+ it "returns attributes in camelLower case" do
53
+ jat_class.attribute(:foo_bar) { 1 }
54
+
55
+ assert_equal({fooBar: 1}, jat_class.to_h(true).dig(:data, :attributes))
56
+ end
57
+
58
+ it "accepts `fields` in camelLower format" do
59
+ jat_class.attribute(:foo_bar, exposed: false) { 1 }
60
+
61
+ response = jat_class.to_h(true, params: {fields: {foo: "fooBar"}})
62
+ assert_equal({fooBar: 1}, response.dig(:data, :attributes))
63
+ end
64
+
65
+ it "accepts `include` in camelLower format" do
66
+ new_serializer = Class.new(Jat)
67
+ new_serializer.plugin(:json_api)
68
+ new_serializer.type :new
69
+ new_serializer.attribute(:id) { object }
70
+
71
+ jat_class.relationship(:foo_bar, serializer: new_serializer) { 1 }
72
+
73
+ response = jat_class.to_h(true, params: {include: "fooBar"})
74
+ response_relationships = response.dig(:data, :relationships).keys
75
+ assert_includes(response_relationships, :fooBar)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApi" do
6
+ let(:jat_class) do
7
+ new_class = Class.new(Jat)
8
+ new_class.plugin(plugin)
9
+ new_class.type :jat
10
+ new_class.attribute :id, key: :itself
11
+ new_class
12
+ end
13
+
14
+ let(:plugin) { Jat::Plugins.load_plugin(:json_api) }
15
+
16
+ describe ".after_load" do
17
+ it "loads _json_api_activerecord plugin if activerecord option provided" do
18
+ jat_class = Class.new(Jat)
19
+ jat_class.expects(:plugin).with(:_json_api_activerecord, activerecord: true)
20
+
21
+ Jat::Plugins.after_load(plugin, jat_class, activerecord: true)
22
+ end
23
+
24
+ it "registers Presenters constants" do
25
+ jat_class = Class.new(Jat)
26
+ Jat::Plugins.after_load(plugin, jat_class, activerecord: true)
27
+
28
+ assert_equal jat_class::JsonapiPresenter.jat_class, jat_class
29
+ assert_equal jat_class::LinksPresenter.jat_class, jat_class
30
+ assert_equal jat_class::DocumentLinksPresenter.jat_class, jat_class
31
+ assert_equal jat_class::RelationshipLinksPresenter.jat_class, jat_class
32
+ assert_equal jat_class::MetaPresenter.jat_class, jat_class
33
+ assert_equal jat_class::DocumentMetaPresenter.jat_class, jat_class
34
+ assert_equal jat_class::RelationshipMetaPresenter.jat_class, jat_class
35
+ end
36
+ end
37
+
38
+ describe "InstanceMethods" do
39
+ let(:jat) do
40
+ jat_class.new("JAT", {})
41
+ end
42
+
43
+ describe "#to_h" do
44
+ it "returns response in json-api format" do
45
+ expected_result = {data: {type: :jat, id: "JAT"}}
46
+ assert_equal expected_result, jat.to_h
47
+ end
48
+ end
49
+
50
+ describe "#traversal_map" do
51
+ it "returns memorized traversal_map object" do
52
+ assert_equal jat.traversal_map.class, Jat::Plugins::JsonApi::TraversalMap
53
+ assert_same jat.traversal_map, jat.traversal_map
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "ClassMethods" do
59
+ describe ".relationship" do
60
+ it "adds new attribute with required serializer" do
61
+ jat_class.relationship(:foo, serializer: jat_class, exposed: true) { "block" }
62
+
63
+ atribute = jat_class.attributes[:foo]
64
+ assert_equal jat_class, atribute.serializer
65
+ assert_equal true, atribute.exposed?
66
+ assert_equal "block", jat_class::Presenter.new(nil, nil).foo
67
+ end
68
+ end
69
+
70
+ describe ".inherited" do
71
+ it "inherits type" do
72
+ child = Class.new(jat_class)
73
+ assert_equal :jat, child.type
74
+ end
75
+
76
+ it "inherits `jsonapi` data" do
77
+ jat_class.jsonapi(:version) { "1.0" }
78
+
79
+ child = Class.new(jat_class)
80
+ assert_equal("1.0", child.jsonapi_data[:version].call)
81
+ end
82
+
83
+ it "inherits `object_links`, `relationship_links`, `document_links`" do
84
+ jat_class.object_link(:self) { "/articles/1" }
85
+ jat_class.relationship_link(:related) { "/articles/2" }
86
+ jat_class.document_link(:last) { "/articles/3" }
87
+ child = Class.new(jat_class)
88
+
89
+ assert_equal("/articles/1", child.object_links[:self].call)
90
+ assert_equal("/articles/2", child.relationship_links[:related].call)
91
+ assert_equal("/articles/3", child.document_links[:last].call)
92
+ end
93
+
94
+ it "does not change parents links when children links are changed" do
95
+ jat_class.object_link(:self) { "/articles/1" }
96
+ jat_class.relationship_link(:related) { "/articles/2" }
97
+ jat_class.document_link(:last) { "/articles/3" }
98
+
99
+ child = Class.new(jat_class)
100
+ child.object_links.delete(:self)
101
+ child.relationship_links.delete(:related)
102
+ child.document_links.delete(:last)
103
+
104
+ assert_equal("/articles/1", jat_class.object_links[:self].call)
105
+ assert_equal("/articles/2", jat_class.relationship_links[:related].call)
106
+ assert_equal("/articles/3", jat_class.document_links[:last].call)
107
+ end
108
+
109
+ it "inherits `object_meta`, `relationship_meta`, `document_meta`" do
110
+ jat_class.object_meta(:self) { "foo/1" }
111
+ jat_class.relationship_meta(:related) { "foo/2" }
112
+ jat_class.document_meta(:last) { "foo/3" }
113
+ child = Class.new(jat_class)
114
+
115
+ assert_equal("foo/1", child.added_object_meta[:self].call)
116
+ assert_equal("foo/2", child.added_relationship_meta[:related].call)
117
+ assert_equal("foo/3", child.added_document_meta[:last].call)
118
+ end
119
+
120
+ it "does not change parents meta when children meta changed" do
121
+ jat_class.object_meta(:self) { "foo/1" }
122
+ jat_class.relationship_meta(:related) { "foo/2" }
123
+ jat_class.document_meta(:last) { "foo/3" }
124
+
125
+ child = Class.new(jat_class)
126
+ child.added_object_meta.delete(:self)
127
+ child.added_relationship_meta.delete(:related)
128
+ child.added_document_meta.delete(:last)
129
+
130
+ assert_equal("foo/1", jat_class.added_object_meta[:self].call)
131
+ assert_equal("foo/2", jat_class.added_relationship_meta[:related].call)
132
+ assert_equal("foo/3", jat_class.added_document_meta[:last].call)
133
+ end
134
+ end
135
+
136
+ describe ".type" do
137
+ it "does not allows to ask for type before type is defined" do
138
+ new_class = Class.new(Jat) { plugin(:json_api) }
139
+
140
+ error = assert_raises(Jat::Error) { new_class.type }
141
+ assert_equal "#{new_class} has no defined type", error.message
142
+ end
143
+
144
+ it "saves and returns current type" do
145
+ assert_equal :jat, jat_class.type
146
+ end
147
+
148
+ it "symbolizes type" do
149
+ jat_class.type "users"
150
+ assert_equal :users, jat_class.type
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApi::ConstructTraversalMap" do
6
+ before { Jat::Plugins.load_plugin(:json_api) }
7
+
8
+ let(:described_class) { Jat::Plugins::JsonApi::ConstructTraversalMap }
9
+ let(:base_class) { Class.new(Jat) { plugin :json_api } }
10
+
11
+ let(:a) do
12
+ ser = Class.new(base_class)
13
+ ser.type :a
14
+
15
+ ser.attribute :a1
16
+ ser.attribute :a2
17
+ ser.attribute :a3, exposed: false
18
+
19
+ ser.relationship :b, serializer: b
20
+ ser.relationship :c, serializer: c
21
+ ser.relationship :d, serializer: d, exposed: true
22
+ ser
23
+ end
24
+
25
+ let(:b) do
26
+ ser = Class.new(base_class)
27
+ ser.type :b
28
+ ser.attribute :b1
29
+ ser.attribute :b2
30
+ ser.attribute :b3, exposed: false
31
+ ser
32
+ end
33
+
34
+ let(:c) do
35
+ ser = Class.new(base_class)
36
+ ser.type :c
37
+ ser.attribute :c1
38
+ ser.attribute :c2
39
+ ser.attribute :c3, exposed: false
40
+ ser
41
+ end
42
+
43
+ let(:d) do
44
+ ser = Class.new(base_class)
45
+ ser.type :d
46
+ ser.attribute :d1
47
+ ser.attribute :d2
48
+ ser.attribute :d3, exposed: false
49
+ ser
50
+ end
51
+
52
+ it "returns all attributes" do
53
+ result = described_class.new(a, :all).to_h
54
+ expected_result = {
55
+ a: {serializer: a, attributes: %i[a1 a2 a3], relationships: %i[b c d]},
56
+ b: {serializer: b, attributes: %i[b1 b2 b3], relationships: []},
57
+ c: {serializer: c, attributes: %i[c1 c2 c3], relationships: []},
58
+ d: {serializer: d, attributes: %i[d1 d2 d3], relationships: []}
59
+ }
60
+
61
+ assert_equal expected_result, result
62
+ end
63
+
64
+ it "returns exposed attributes" do
65
+ result = described_class.new(a, :exposed).to_h
66
+ expected_result = {
67
+ a: {serializer: a, attributes: %i[a1 a2], relationships: %i[d]},
68
+ d: {serializer: d, attributes: %i[d1 d2], relationships: []}
69
+ }
70
+
71
+ assert_equal expected_result, result
72
+ end
73
+
74
+ it "returns only manually exposed per-type attributes or exposed by default when no manual type provided" do
75
+ exposed = {
76
+ a: %i[a2 a3 c d],
77
+ c: %i[c2 c3],
78
+ d: %i[d2 d3]
79
+ }
80
+ result = described_class.new(a, :manual, manually_exposed: exposed).to_h
81
+ expected_result = {
82
+ a: {serializer: a, attributes: %i[a2 a3], relationships: %i[c d]},
83
+ c: {serializer: c, attributes: %i[c2 c3], relationships: []},
84
+ d: {serializer: d, attributes: %i[d2 d3], relationships: []}
85
+ }
86
+
87
+ assert_equal expected_result, result
88
+ end
89
+
90
+ it "returns manually exposed per-type attributes or exposed by default when no manual type provided" do
91
+ exposed = {
92
+ a: %i[a2 a3 b c],
93
+ c: %i[c2 c3]
94
+ }
95
+ result = described_class.new(a, :manual, manually_exposed: exposed).to_h
96
+ expected_result = {
97
+ a: {serializer: a, attributes: %i[a2 a3], relationships: %i[b c]},
98
+ b: {serializer: b, attributes: %i[b1 b2], relationships: []},
99
+ c: {serializer: c, attributes: %i[c2 c3], relationships: []}
100
+ }
101
+
102
+ assert_equal expected_result, result
103
+ end
104
+
105
+ it "returns combined auto-exposed and manualy exposed attributes" do
106
+ exposed = {
107
+ a: %i[c],
108
+ c: %i[c3]
109
+ }
110
+ result = described_class.new(a, :exposed, manually_exposed: exposed).to_h
111
+ expected_result = {
112
+ a: {serializer: a, attributes: %i[a1 a2], relationships: %i[c d]},
113
+ c: {serializer: c, attributes: %i[c1 c2 c3], relationships: []},
114
+ d: {serializer: d, attributes: %i[d1 d2], relationships: []}
115
+ }
116
+
117
+ assert_equal expected_result, result
118
+ end
119
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApi::Map" do
6
+ subject { Jat::Plugins::JsonApi::Map.call(jat) }
7
+
8
+ let(:jat_class) do
9
+ Class.new(Jat) do
10
+ plugin :json_api
11
+ type :jat
12
+ end
13
+ end
14
+ let(:jat) { jat_class.new(nil, params: {fields: param_fields, include: param_includes}) }
15
+
16
+ let(:default_map) { {a: :a1, b: :b1, c: :c1} }
17
+ let(:includes_map) { {b: :b2, c: :c2} }
18
+ let(:fields_map) { {c: :c3} }
19
+
20
+ before do
21
+ jat.traversal_map.expects(:exposed).returns(default_map)
22
+ end
23
+
24
+ describe "when no params given" do
25
+ let(:param_includes) { nil }
26
+ let(:param_fields) { nil }
27
+
28
+ it "returns map of exposed by default fields" do
29
+ assert_equal default_map, subject
30
+ end
31
+ end
32
+
33
+ describe "when fields given" do
34
+ let(:param_includes) { nil }
35
+ let(:param_fields) { "FIELDS" }
36
+
37
+ before do
38
+ constructor = Jat::Plugins::JsonApi::ConstructTraversalMap.allocate
39
+ constructor.expects(:to_h).returns(fields_map)
40
+
41
+ Jat::Plugins::JsonApi::Params::Fields
42
+ .expects(:call)
43
+ .with(jat, "FIELDS")
44
+ .returns("PARSED_FIELDS")
45
+
46
+ Jat::Plugins::JsonApi::ConstructTraversalMap
47
+ .expects(:new)
48
+ .with(jat_class, :manual, manually_exposed: "PARSED_FIELDS")
49
+ .returns(constructor)
50
+ end
51
+
52
+ it "constructs map with default and provided fields" do
53
+ assert_equal({a: :a1, b: :b1, c: :c3}, subject)
54
+ end
55
+ end
56
+
57
+ describe "when includes given" do
58
+ let(:param_includes) { "INCLUDES" }
59
+ let(:param_fields) { nil }
60
+
61
+ before do
62
+ constructor = Jat::Plugins::JsonApi::ConstructTraversalMap.allocate
63
+ constructor.expects(:to_h).returns(includes_map)
64
+
65
+ Jat::Plugins::JsonApi::Params::Include
66
+ .expects(:call)
67
+ .with(jat, "INCLUDES")
68
+ .returns("PARSED_INCLUDES")
69
+
70
+ Jat::Plugins::JsonApi::ConstructTraversalMap
71
+ .expects(:new)
72
+ .with(jat_class, :exposed, manually_exposed: "PARSED_INCLUDES")
73
+ .returns(constructor)
74
+ end
75
+
76
+ it "constructs map with default and included fields" do
77
+ assert_equal({a: :a1, b: :b2, c: :c2}, subject)
78
+ end
79
+ end
80
+
81
+ describe "when fields and includes given" do
82
+ let(:param_fields) { "FIELDS" }
83
+ let(:param_includes) { "INCLUDES" }
84
+
85
+ before do
86
+ constructor1 = Jat::Plugins::JsonApi::ConstructTraversalMap.allocate
87
+ constructor1.expects(:to_h).returns(fields_map)
88
+
89
+ Jat::Plugins::JsonApi::Params::Fields
90
+ .expects(:call)
91
+ .with(jat, "FIELDS")
92
+ .returns("PARSED_FIELDS")
93
+
94
+ Jat::Plugins::JsonApi::ConstructTraversalMap
95
+ .expects(:new)
96
+ .with(jat_class, :manual, manually_exposed: "PARSED_FIELDS")
97
+ .returns(constructor1)
98
+
99
+ constructor2 = Jat::Plugins::JsonApi::ConstructTraversalMap.allocate
100
+ constructor2.expects(:to_h).returns(includes_map)
101
+
102
+ Jat::Plugins::JsonApi::Params::Include
103
+ .expects(:call)
104
+ .with(jat, "INCLUDES")
105
+ .returns("PARSED_INCLUDES")
106
+
107
+ Jat::Plugins::JsonApi::ConstructTraversalMap
108
+ .expects(:new)
109
+ .with(jat_class, :exposed, manually_exposed: "PARSED_INCLUDES")
110
+ .returns(constructor2)
111
+ end
112
+
113
+ it "constructs map with using everything: defaults, includes, fields" do
114
+ assert_equal({a: :a1, b: :b2, c: :c3}, subject)
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApi::Params::Fields::Parse" do
6
+ before { Jat::Plugins.load_plugin(:json_api) }
7
+
8
+ let(:described_class) { Jat::Plugins::JsonApi::Params::Fields::Parse }
9
+
10
+ it "returns empty hash when param not provided" do
11
+ result = described_class.call(nil)
12
+ assert_equal({}, result)
13
+ end
14
+
15
+ it "returns hash with parsed keys" do
16
+ result = described_class.call(a: "a1,a2", b: "b1")
17
+ assert_equal({a: %i[a1 a2], b: %i[b1]}, result)
18
+ end
19
+
20
+ it "symbolizes types" do
21
+ result = described_class.call("a" => "a1,a2", "b" => "b1")
22
+ assert_equal({a: %i[a1 a2], b: %i[b1]}, result)
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApi::Params::Fields::Validate" do
6
+ before { Jat::Plugins.load_plugin(:json_api) }
7
+
8
+ let(:described_class) { Jat::Plugins::JsonApi::Params::Fields::Validate }
9
+ let(:base_class) { Class.new(Jat) { plugin :json_api } }
10
+ let(:a_serializer) { Class.new(base_class) }
11
+ let(:b_serializer) { Class.new(base_class) }
12
+
13
+ before do
14
+ ser = a_serializer
15
+ ser.type :a
16
+ ser.attribute :a1
17
+ ser.relationship :a2, serializer: b_serializer
18
+
19
+ ser = b_serializer
20
+ ser.type :b
21
+ ser.attribute :b1
22
+ ser.relationship :b2, serializer: a_serializer
23
+ end
24
+
25
+ it "does not raises when serializer has all requested keys" do
26
+ assert described_class.call(a_serializer.allocate, a: %i[a1 a2], b: %i[b1 b2])
27
+ end
28
+
29
+ it "does not raises when requested only fields for nested serializer" do
30
+ assert described_class.call(a_serializer.allocate, b: %i[b1 b2])
31
+ end
32
+
33
+ it "raises error when some type can not be in response" do
34
+ error = assert_raises(Jat::Error) { described_class.call(a_serializer.allocate, a: %i[a1 a2], foo: %i[b1 b2]) }
35
+ assert_equal "#{a_serializer} and its children have no requested type `foo`", error.message
36
+ end
37
+
38
+ it "raises error when some key is not present in main serializer" do
39
+ error = assert_raises(Jat::Error) { described_class.call(a_serializer.allocate, a: %i[b1]) }
40
+ assert_equal "#{a_serializer} has no requested attribute or relationship `b1`", error.message
41
+ end
42
+
43
+ it "raises error when some key is not present in nested serializer" do
44
+ error = assert_raises(Jat::Error) { described_class.call(a_serializer.allocate, a: %i[a1], b: %i[a1]) }
45
+ assert_equal "#{b_serializer} has no requested attribute or relationship `a1`", error.message
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApi::Params::Fields" do
6
+ before { Jat::Plugins.load_plugin(:json_api) }
7
+
8
+ let(:jat_class) do
9
+ Class.new(Jat) do
10
+ plugin :json_api
11
+ type :a
12
+
13
+ attribute :a1
14
+ attribute :a2
15
+ attribute :a3
16
+ end
17
+ end
18
+
19
+ let(:described_class) { Jat::Plugins::JsonApi::Params::Fields }
20
+
21
+ it "returns empty hash when parameters not provided" do
22
+ result = described_class.call(jat_class.allocate, nil)
23
+
24
+ assert_equal({}, result)
25
+ end
26
+
27
+ it "returns parsed attributes" do
28
+ result = described_class.call(jat_class.allocate, a: "a1,a2")
29
+
30
+ assert_equal({a: %i[a1 a2]}, result)
31
+ end
32
+
33
+ it "validates provided attributes" do
34
+ error = assert_raises(Jat::Error) { described_class.call(jat_class.allocate, a: "a1,a2,a3,a4") }
35
+ assert_match(/a4/, error.message)
36
+ end
37
+ end