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.
- checksums.yaml +4 -4
- data/README.md +13 -104
- data/lib/jsonapi/realizer.rb +26 -25
- data/lib/jsonapi/realizer/action.rb +2 -2
- data/lib/jsonapi/realizer/adapter.rb +21 -58
- data/lib/jsonapi/realizer/adapter/active_record.rb +38 -17
- data/lib/jsonapi/realizer/adapter_spec.rb +3 -2
- data/lib/jsonapi/realizer/configuration.rb +22 -0
- data/lib/jsonapi/realizer/context.rb +8 -0
- data/lib/jsonapi/realizer/controller.rb +55 -0
- data/lib/jsonapi/realizer/error.rb +10 -9
- data/lib/jsonapi/realizer/error/invalid_content_type_header.rb +5 -0
- data/lib/jsonapi/realizer/error/invalid_data_type_property.rb +13 -0
- data/lib/jsonapi/realizer/error/invalid_root_property.rb +13 -0
- data/lib/jsonapi/realizer/error/{duplicate_registration.rb → missing_data_type_property.rb} +1 -1
- data/lib/jsonapi/realizer/error/resource_attribute_not_found.rb +14 -0
- data/lib/jsonapi/realizer/error/resource_relationship_not_found.rb +14 -0
- data/lib/jsonapi/realizer/resource.rb +278 -73
- data/lib/jsonapi/realizer/resource/attribute.rb +23 -0
- data/lib/jsonapi/realizer/resource/configuration.rb +27 -0
- data/lib/jsonapi/realizer/resource/relation.rb +31 -0
- data/lib/jsonapi/realizer/resource_spec.rb +55 -8
- data/lib/jsonapi/realizer/version.rb +1 -1
- data/lib/jsonapi/realizer_spec.rb +22 -119
- metadata +70 -20
- data/lib/jsonapi/realizer/action/create.rb +0 -36
- data/lib/jsonapi/realizer/action/create_spec.rb +0 -165
- data/lib/jsonapi/realizer/action/destroy.rb +0 -27
- data/lib/jsonapi/realizer/action/destroy_spec.rb +0 -81
- data/lib/jsonapi/realizer/action/index.rb +0 -29
- data/lib/jsonapi/realizer/action/index_spec.rb +0 -75
- data/lib/jsonapi/realizer/action/show.rb +0 -35
- data/lib/jsonapi/realizer/action/show_spec.rb +0 -81
- data/lib/jsonapi/realizer/action/update.rb +0 -37
- data/lib/jsonapi/realizer/action/update_spec.rb +0 -170
- data/lib/jsonapi/realizer/action_spec.rb +0 -46
- data/lib/jsonapi/realizer/adapter/memory.rb +0 -31
- data/lib/jsonapi/realizer/error/invalid_accept_header.rb +0 -9
- data/lib/jsonapi/realizer/error/malformed_data_root_property.rb +0 -9
- data/lib/jsonapi/realizer/error/missing_accept_header.rb +0 -9
- 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
|