jsonapi-realizer 4.4.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -104
  3. data/lib/jsonapi/realizer.rb +26 -25
  4. data/lib/jsonapi/realizer/action.rb +2 -2
  5. data/lib/jsonapi/realizer/adapter.rb +21 -58
  6. data/lib/jsonapi/realizer/adapter/active_record.rb +38 -17
  7. data/lib/jsonapi/realizer/adapter_spec.rb +3 -2
  8. data/lib/jsonapi/realizer/configuration.rb +22 -0
  9. data/lib/jsonapi/realizer/context.rb +8 -0
  10. data/lib/jsonapi/realizer/controller.rb +55 -0
  11. data/lib/jsonapi/realizer/error.rb +10 -9
  12. data/lib/jsonapi/realizer/error/invalid_content_type_header.rb +5 -0
  13. data/lib/jsonapi/realizer/error/invalid_data_type_property.rb +13 -0
  14. data/lib/jsonapi/realizer/error/invalid_root_property.rb +13 -0
  15. data/lib/jsonapi/realizer/error/{duplicate_registration.rb → missing_data_type_property.rb} +1 -1
  16. data/lib/jsonapi/realizer/error/resource_attribute_not_found.rb +14 -0
  17. data/lib/jsonapi/realizer/error/resource_relationship_not_found.rb +14 -0
  18. data/lib/jsonapi/realizer/resource.rb +278 -73
  19. data/lib/jsonapi/realizer/resource/attribute.rb +23 -0
  20. data/lib/jsonapi/realizer/resource/configuration.rb +27 -0
  21. data/lib/jsonapi/realizer/resource/relation.rb +31 -0
  22. data/lib/jsonapi/realizer/resource_spec.rb +55 -8
  23. data/lib/jsonapi/realizer/version.rb +1 -1
  24. data/lib/jsonapi/realizer_spec.rb +22 -119
  25. metadata +70 -20
  26. data/lib/jsonapi/realizer/action/create.rb +0 -36
  27. data/lib/jsonapi/realizer/action/create_spec.rb +0 -165
  28. data/lib/jsonapi/realizer/action/destroy.rb +0 -27
  29. data/lib/jsonapi/realizer/action/destroy_spec.rb +0 -81
  30. data/lib/jsonapi/realizer/action/index.rb +0 -29
  31. data/lib/jsonapi/realizer/action/index_spec.rb +0 -75
  32. data/lib/jsonapi/realizer/action/show.rb +0 -35
  33. data/lib/jsonapi/realizer/action/show_spec.rb +0 -81
  34. data/lib/jsonapi/realizer/action/update.rb +0 -37
  35. data/lib/jsonapi/realizer/action/update_spec.rb +0 -170
  36. data/lib/jsonapi/realizer/action_spec.rb +0 -46
  37. data/lib/jsonapi/realizer/adapter/memory.rb +0 -31
  38. data/lib/jsonapi/realizer/error/invalid_accept_header.rb +0 -9
  39. data/lib/jsonapi/realizer/error/malformed_data_root_property.rb +0 -9
  40. data/lib/jsonapi/realizer/error/missing_accept_header.rb +0 -9
  41. data/lib/jsonapi/realizer/error/missing_type_resource_property.rb +0 -9
@@ -1,36 +0,0 @@
1
- module JSONAPI
2
- module Realizer
3
- class Action
4
- class Create < Action
5
- attr_accessor :resource
6
-
7
- def initialize(payload:, headers:, scope: nil)
8
- raise Error::MissingContentTypeHeader unless headers.key?("Content-Type")
9
- raise Error::InvalidContentTypeHeader unless headers.fetch("Content-Type") == JSONAPI::MEDIA_TYPE
10
-
11
- super(payload: payload, headers: headers, scope: scope)
12
-
13
- @resource = resource_class.new(relation.new)
14
-
15
- raise Error::MissingRootProperty unless payload.key?("data") || payload.key?("errors") || payload.key?("meta")
16
- raise Error::MissingTypeResourceProperty if payload.key?("data") && data.kind_of?(Hash) && !data.key?("type")
17
- raise Error::MissingTypeResourceProperty if payload.key?("data") && data.kind_of?(Array) && !data.all? {|resource| resource.key?("type")}
18
- end
19
-
20
- def call
21
- adapter.assign_attributes_via_call(resource.model, {id: id}) if id
22
- adapter.assign_attributes_via_call(resource.model, attributes)
23
- adapter.assign_relationships_via_call(resource.model, relationships)
24
- end
25
-
26
- def model
27
- resource.model
28
- end
29
-
30
- private def id
31
- payload.fetch("id", nil)
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,165 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe JSONAPI::Realizer::Action::Create do
4
- let(:action) { described_class.new(payload: payload, headers: headers) }
5
-
6
- describe "#call" do
7
- subject { action.tap(&:call) }
8
-
9
- context "with no top-level data and no content-type header no accept headers" do
10
- let(:payload) do
11
- {}
12
- end
13
- let(:headers) do
14
- {}
15
- end
16
-
17
- it "raises an exception" do
18
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::MissingContentTypeHeader)
19
- end
20
- end
21
-
22
- context "with no top-level data and good content-type header no accept headers" do
23
- let(:payload) do
24
- {}
25
- end
26
- let(:headers) do
27
- {
28
- "Content-Type" => "application/vnd.api+json",
29
- }
30
- end
31
-
32
- it "raises an exception" do
33
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::MissingAcceptHeader)
34
- end
35
- end
36
-
37
- context "with no top-level data and wrong content-type header" do
38
- let(:payload) do
39
- {}
40
- end
41
- let(:headers) do
42
- {
43
- "Content-Type" => "application/json"
44
- }
45
- end
46
-
47
- it "raises an exception" do
48
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::InvalidContentTypeHeader)
49
- end
50
- end
51
-
52
- context "with no top-level data and good content-type header and wrong accept header" do
53
- let(:payload) do
54
- {}
55
- end
56
- let(:headers) do
57
- {
58
- "Content-Type" => "application/vnd.api+json",
59
- "Accept" => "application/json"
60
- }
61
- end
62
-
63
- it "raises an exception" do
64
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::InvalidAcceptHeader)
65
- end
66
- end
67
-
68
- context "with wrong top-level data and good headers" do
69
- let(:payload) do
70
- {
71
- "data" => ""
72
- }
73
- end
74
- let(:headers) do
75
- {
76
- "Content-Type" => "application/vnd.api+json",
77
- "Accept" => "application/vnd.api+json"
78
- }
79
- end
80
-
81
- it "raises an exception" do
82
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::MalformedDataRootProperty)
83
- end
84
- end
85
-
86
- context "with a good payload and good headers" do
87
- let(:payload) do
88
- {
89
- "data" => {
90
- "id" => "550e8400-e29b-41d4-a716-446655440000",
91
- "type" => "photos",
92
- "attributes" => {
93
- "title" => "Ember Hamster",
94
- "alt-text" => "A hamster logo.",
95
- "src" => "http://example.com/images/productivity.png"
96
- },
97
- "relationships" => {
98
- "active-photographer" => {
99
- "data" => { "type" => "photographer-accounts", "id" => "4b8a0af6-953d-4729-8b9a-1fa4eb18f3c9" }
100
- }
101
- }
102
- }
103
- }
104
- end
105
-
106
- let(:headers) do
107
- {
108
- "Content-Type" => "application/vnd.api+json",
109
- "Accept" => "application/vnd.api+json"
110
- }
111
- end
112
-
113
- shared_examples "api" do
114
- it "is the right model" do
115
- subject
116
-
117
- expect(action.model).to be_a_kind_of(Photo)
118
- end
119
-
120
- it "assigns the title attribute" do
121
- subject
122
-
123
- expect(action.model).to have_attributes(title: "Ember Hamster")
124
- end
125
-
126
- it "assigns the alt_text attribute" do
127
- subject
128
-
129
- expect(action.model).to have_attributes(alt_text: "A hamster logo.")
130
- end
131
-
132
- it "assigns the src attribute" do
133
- subject
134
-
135
- expect(action.model).to have_attributes(src: "http://example.com/images/productivity.png")
136
- end
137
-
138
- it "assigns the active_photographer attribute" do
139
- subject
140
-
141
- expect(action.model).to have_attributes(active_photographer: a_kind_of(Account))
142
- end
143
- end
144
-
145
- context "in a memory store", memory: true do
146
- before do
147
- Account::STORE["4b8a0af6-953d-4729-8b9a-1fa4eb18f3c9"] = {
148
- id: "4b8a0af6-953d-4729-8b9a-1fa4eb18f3c9",
149
- name: "Kurtis Rainbolt-Greene"
150
- }
151
- end
152
-
153
- include_examples "api"
154
-
155
- it "creates the new record" do
156
- expect(subject.model).to have_attributes(
157
- title: "Ember Hamster",
158
- alt_text: "A hamster logo.",
159
- src: "http://example.com/images/productivity.png"
160
- )
161
- end
162
- end
163
- end
164
- end
165
- end
@@ -1,27 +0,0 @@
1
- module JSONAPI
2
- module Realizer
3
- class Action
4
- class Destroy < Action
5
- attr_accessor :resource
6
-
7
- def initialize(payload:, headers:, scope: nil, type:)
8
- @type = type
9
-
10
- super(payload: payload, headers: headers, scope: scope)
11
-
12
- @resource = resource_class.new(adapter.find_via_call(relation, id))
13
- end
14
-
15
- def model
16
- resource.model
17
- end
18
-
19
- private def id
20
- return data.fetch("id", nil) if data
21
-
22
- payload.fetch("id", nil)
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,81 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe JSONAPI::Realizer::Action::Destroy do
4
- let(:action) { described_class.new(payload: payload, headers: headers, type: :photos) }
5
-
6
- describe "#model" do
7
- subject { action.model }
8
-
9
- context "with no top-level data and good content-type header no accept headers" do
10
- let(:payload) do
11
- {}
12
- end
13
- let(:headers) do
14
- {
15
- "Content-Type" => "application/vnd.api+json",
16
- }
17
- end
18
-
19
- it "raises an exception" do
20
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::MissingAcceptHeader)
21
- end
22
- end
23
-
24
- context "with no top-level data and good content-type header and wrong accept header" do
25
- let(:payload) do
26
- {}
27
- end
28
- let(:headers) do
29
- {
30
- "Content-Type" => "application/vnd.api+json",
31
- "Accept" => "application/json"
32
- }
33
- end
34
-
35
- it "raises an exception" do
36
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::InvalidAcceptHeader)
37
- end
38
- end
39
-
40
- context "with a good payload and good headers" do
41
- let(:payload) do
42
- {
43
- "id" => "d09ae4c6-0fc3-4c42-8fe8-6029530c3bed"
44
- }
45
- end
46
- let(:headers) do
47
- {
48
- "Content-Type" => "application/vnd.api+json",
49
- "Accept" => "application/vnd.api+json"
50
- }
51
- end
52
-
53
- shared_examples "api" do
54
- it "returns a photo model" do
55
- expect(subject).to be_a_kind_of(Photo)
56
- end
57
-
58
- it "returns the photos attributes" do
59
- expect(subject).to have_attributes(title: "Ember Fox", src: "http://example.com/images/productivity-2.png")
60
- end
61
- end
62
-
63
- context "in a memory store", memory: true do
64
- before do
65
- Photo::STORE["550e8400-e29b-41d4-a716-446655440000"] = {
66
- id: "550e8400-e29b-41d4-a716-446655440000",
67
- title: "Ember Hamster",
68
- src: "http://example.com/images/productivity.png"
69
- }
70
- Photo::STORE["d09ae4c6-0fc3-4c42-8fe8-6029530c3bed"] = {
71
- id: "d09ae4c6-0fc3-4c42-8fe8-6029530c3bed",
72
- title: "Ember Fox",
73
- src: "http://example.com/images/productivity-2.png"
74
- }
75
- end
76
-
77
- include_examples "api"
78
- end
79
- end
80
- end
81
- end
@@ -1,29 +0,0 @@
1
- module JSONAPI
2
- module Realizer
3
- class Action
4
- class Index < Action
5
- attr_accessor :resources
6
-
7
- def initialize(payload:, headers:, scope: nil, type:)
8
- @type = type
9
-
10
- super(payload: payload, headers: headers, scope: scope)
11
-
12
- @resources = adapter.find_many_via_call(relation).map(&resource_class.method(:new))
13
- end
14
-
15
- def models
16
- resources.map(&:model)
17
- end
18
-
19
- private def data
20
- payload["data"]
21
- end
22
-
23
- private def type
24
- @type.to_s.dasherize if @type
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,75 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe JSONAPI::Realizer::Action::Index do
4
- let(:action) { described_class.new(payload: payload, headers: headers, type: :photos) }
5
-
6
- describe "#models" do
7
- subject { action.models }
8
-
9
- context "with no top-level data and good content-type header no accept headers" do
10
- let(:payload) do
11
- {}
12
- end
13
- let(:headers) do
14
- {
15
- "Content-Type" => "application/vnd.api+json",
16
- }
17
- end
18
-
19
- it "raises an exception" do
20
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::MissingAcceptHeader)
21
- end
22
- end
23
-
24
- context "with no top-level data and good content-type header and wrong accept header" do
25
- let(:payload) do
26
- {}
27
- end
28
- let(:headers) do
29
- {
30
- "Content-Type" => "application/vnd.api+json",
31
- "Accept" => "application/json"
32
- }
33
- end
34
-
35
- it "raises an exception" do
36
- expect {subject}.to raise_exception(JSONAPI::Realizer::Error::InvalidAcceptHeader)
37
- end
38
- end
39
-
40
- context "with a good payload and good headers" do
41
- let(:payload) do
42
- {}
43
- end
44
- let(:headers) do
45
- {
46
- "Content-Type" => "application/vnd.api+json",
47
- "Accept" => "application/vnd.api+json"
48
- }
49
- end
50
-
51
- shared_examples "api" do
52
- it "returns a list of photos" do
53
- expect(subject).to include(a_kind_of(Photo), a_kind_of(Photo))
54
- end
55
- end
56
-
57
- context "in a memory store", memory: true do
58
- before do
59
- Photo::STORE["550e8400-e29b-41d4-a716-446655440000"] = {
60
- id: "550e8400-e29b-41d4-a716-446655440000",
61
- title: "Ember Hamster",
62
- src: "http://example.com/images/productivity.png"
63
- }
64
- Photo::STORE["d09ae4c6-0fc3-4c42-8fe8-6029530c3bed"] = {
65
- id: "d09ae4c6-0fc3-4c42-8fe8-6029530c3bed",
66
- title: "Ember Fox",
67
- src: "http://example.com/images/productivity-2.png"
68
- }
69
- end
70
-
71
- include_examples "api"
72
- end
73
- end
74
- end
75
- end
@@ -1,35 +0,0 @@
1
- module JSONAPI
2
- module Realizer
3
- class Action
4
- class Show < Action
5
- attr_accessor :resource
6
-
7
- def initialize(payload:, headers:, scope: nil, type:)
8
- @type = type
9
-
10
- super(payload: payload, headers: headers, scope: scope)
11
-
12
- @resource = resource_class.new(adapter.find_via_call(relation, id))
13
- end
14
-
15
- def model
16
- resource.model
17
- end
18
-
19
- private def data
20
- payload["data"]
21
- end
22
-
23
- private def type
24
- @type.to_s.dasherize if @type
25
- end
26
-
27
- private def id
28
- return data.fetch("id", nil) if data
29
-
30
- payload.fetch("id", nil)
31
- end
32
- end
33
- end
34
- end
35
- end