jsonapi-realizer 4.4.0 → 5.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 (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