jsonapi-realizer 3.0.0 → 4.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 +10 -14
- data/lib/jsonapi/realizer/action/create.rb +3 -7
- data/lib/jsonapi/realizer/action/create_spec.rb +81 -31
- data/lib/jsonapi/realizer/action/destroy.rb +27 -0
- data/lib/jsonapi/realizer/action/destroy_spec.rb +81 -0
- data/lib/jsonapi/realizer/action/index.rb +2 -4
- data/lib/jsonapi/realizer/action/index_spec.rb +30 -3
- data/lib/jsonapi/realizer/action/show.rb +2 -5
- data/lib/jsonapi/realizer/action/show_spec.rb +30 -3
- data/lib/jsonapi/realizer/action/update.rb +6 -10
- data/lib/jsonapi/realizer/action/update_spec.rb +6 -28
- data/lib/jsonapi/realizer/action.rb +25 -19
- data/lib/jsonapi/realizer/action_spec.rb +8 -6
- data/lib/jsonapi/realizer/adapter/active_record.rb +0 -8
- data/lib/jsonapi/realizer/adapter/memory.rb +0 -15
- data/lib/jsonapi/realizer/adapter.rb +0 -18
- data/lib/jsonapi/realizer/adapter_spec.rb +0 -8
- data/lib/jsonapi/realizer/error.rb +0 -1
- data/lib/jsonapi/realizer/version.rb +1 -1
- data/lib/jsonapi/realizer.rb +2 -0
- metadata +5 -4
- data/lib/jsonapi/realizer/error/too_many_root_properties.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 233431a77cbb99ea5b1fb51dc5840217f6041eb79e054e23074dbd84fa26517b
|
4
|
+
data.tar.gz: 21000506a07d24308f1f22ebb739a3b1e3863d377a1b9ae424fa8b266ff2f44e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8ab9db2090e5c1208c57c27a63ba5959e33aa5fc27bebfd7d5b3b452d2a16c2e0af23b50e5b5dc8fd12738adfdb39edcab3edb70318db235559cc2850b0b5d0
|
7
|
+
data.tar.gz: d2128c6ddfa8318c441b48e8beb27d785239a8737ccdd9cfa6f758b4de7981247e35680e8643dd636c0f8719fbdbd3a8cb48b243031101c89100a05abf21599d
|
data/README.md
CHANGED
@@ -69,15 +69,13 @@ class PhotosController < ApplicationController
|
|
69
69
|
def index
|
70
70
|
realization = JSONAPI::Realizer.index(params, headers: request.headers, type: :photos)
|
71
71
|
|
72
|
-
|
73
|
-
authorize realization.models
|
74
|
-
|
75
|
-
# See: pundit for `policy_scope()`
|
76
|
-
render json: JSONAPI::Serializer.serialize(policy_scope(record), is_collection: true)
|
72
|
+
render json: JSONAPI::Serializer.serialize(realization.models, is_collection: true)
|
77
73
|
end
|
78
74
|
end
|
79
75
|
```
|
80
76
|
|
77
|
+
Notice that we pass `realization.model` to `ProcessPhotosService`, that's because `jsonapi-realizer` doesn't do the act of saving, creating, or destroying! We just ready up the records for you to handle (including errors).
|
78
|
+
|
81
79
|
### Policies
|
82
80
|
|
83
81
|
Most times you will want to control what a person sees when they as for your data. We have created interfaces for this use-case and we'll show how you can use pundit (or any PORO) to constrain your in/out.
|
@@ -130,11 +128,16 @@ end
|
|
130
128
|
``` ruby
|
131
129
|
class PhotosController < ApplicationController
|
132
130
|
def index
|
133
|
-
realization = JSONAPI::Realizer.index(
|
131
|
+
realization = JSONAPI::Realizer.index(
|
132
|
+
policy(Photo).sanitize(:index, params),
|
133
|
+
headers: request.headers,
|
134
|
+
type: :posts,
|
135
|
+
relation: policy_scope(Photo)
|
136
|
+
)
|
134
137
|
|
135
138
|
# See: pundit for `policy_scope()`
|
136
139
|
# See: pundit for `authorize()`
|
137
|
-
render json: JSONAPI::Serializer.serialize(authorize(
|
140
|
+
render json: JSONAPI::Serializer.serialize(authorize(realization.models), is_collection: true)
|
138
141
|
end
|
139
142
|
end
|
140
143
|
```
|
@@ -152,8 +155,6 @@ An adapter must provide the following interfaces:
|
|
152
155
|
0. `find_many_via`, describes how to find many models
|
153
156
|
0. `assign_attributes_via`, describes how to write a set of properties
|
154
157
|
0. `assign_relationships_via`, describes how to write a set of relationships
|
155
|
-
0. `create_via`, describes how to create the model
|
156
|
-
0. `update_via`, describes how to update the model
|
157
158
|
0. `includes_via`, describes how to eager include related models
|
158
159
|
0. `sparse_fields_via`, describes how to only return certain fields
|
159
160
|
|
@@ -173,11 +174,6 @@ class PhotoRealizer
|
|
173
174
|
model.update_columns(attributes)
|
174
175
|
end
|
175
176
|
|
176
|
-
adapter.create_via do |model|
|
177
|
-
model.save!
|
178
|
-
Rails.cache.write(model.cache_key, model)
|
179
|
-
end
|
180
|
-
|
181
177
|
has_one :photographer, as: :profiles
|
182
178
|
|
183
179
|
has :title
|
@@ -4,14 +4,11 @@ module JSONAPI
|
|
4
4
|
class Create < Action
|
5
5
|
attr_accessor :resource
|
6
6
|
|
7
|
-
def initialize(payload:, headers:)
|
8
|
-
@payload = payload
|
9
|
-
@headers = headers
|
10
|
-
|
7
|
+
def initialize(payload:, headers:, scope: nil)
|
11
8
|
raise Error::MissingContentTypeHeader unless headers.key?("Content-Type")
|
12
|
-
raise Error::InvalidContentTypeHeader unless headers.fetch("Content-Type") ==
|
9
|
+
raise Error::InvalidContentTypeHeader unless headers.fetch("Content-Type") == JSONAPI::MEDIA_TYPE
|
13
10
|
|
14
|
-
super(payload: payload, headers: headers)
|
11
|
+
super(payload: payload, headers: headers, scope: scope)
|
15
12
|
|
16
13
|
@resource = resource_class.new(relation.new)
|
17
14
|
|
@@ -24,7 +21,6 @@ module JSONAPI
|
|
24
21
|
adapter.assign_attributes_via_call(resource.model, {id: id}) if id
|
25
22
|
adapter.assign_attributes_via_call(resource.model, attributes)
|
26
23
|
adapter.assign_relationships_via_call(resource.model, relationships)
|
27
|
-
adapter.create_via_call(resource.model)
|
28
24
|
end
|
29
25
|
|
30
26
|
def model
|
@@ -4,11 +4,84 @@ RSpec.describe JSONAPI::Realizer::Action::Create do
|
|
4
4
|
let(:action) { described_class.new(payload: payload, headers: headers) }
|
5
5
|
|
6
6
|
describe "#call" do
|
7
|
-
subject { action.call }
|
7
|
+
subject { action.tap(&:call) }
|
8
8
|
|
9
|
-
context "with no top-level data and
|
10
|
-
|
11
|
-
|
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
|
12
85
|
|
13
86
|
context "with a good payload and good headers" do
|
14
87
|
let(:payload) do
|
@@ -44,12 +117,6 @@ RSpec.describe JSONAPI::Realizer::Action::Create do
|
|
44
117
|
expect(action.model).to be_a_kind_of(Photo)
|
45
118
|
end
|
46
119
|
|
47
|
-
it "assigns the id attribute" do
|
48
|
-
subject
|
49
|
-
|
50
|
-
expect(action.model).to have_attributes(id: "550e8400-e29b-41d4-a716-446655440000")
|
51
|
-
end
|
52
|
-
|
53
120
|
it "assigns the title attribute" do
|
54
121
|
subject
|
55
122
|
|
@@ -68,12 +135,6 @@ RSpec.describe JSONAPI::Realizer::Action::Create do
|
|
68
135
|
expect(action.model).to have_attributes(src: "http://example.com/images/productivity.png")
|
69
136
|
end
|
70
137
|
|
71
|
-
it "assigns the updated_at attribute" do
|
72
|
-
subject
|
73
|
-
|
74
|
-
expect(action.model).to have_attributes(updated_at: a_kind_of(Time))
|
75
|
-
end
|
76
|
-
|
77
138
|
it "assigns the active_photographer attribute" do
|
78
139
|
subject
|
79
140
|
|
@@ -92,21 +153,10 @@ RSpec.describe JSONAPI::Realizer::Action::Create do
|
|
92
153
|
include_examples "api"
|
93
154
|
|
94
155
|
it "creates the new record" do
|
95
|
-
expect
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
}.from(
|
100
|
-
{}
|
101
|
-
).to(
|
102
|
-
{
|
103
|
-
"550e8400-e29b-41d4-a716-446655440000" => hash_including(
|
104
|
-
id: "550e8400-e29b-41d4-a716-446655440000",
|
105
|
-
title: "Ember Hamster",
|
106
|
-
alt_text: "A hamster logo.",
|
107
|
-
src: "http://example.com/images/productivity.png"
|
108
|
-
)
|
109
|
-
}
|
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"
|
110
160
|
)
|
111
161
|
end
|
112
162
|
end
|
@@ -0,0 +1,27 @@
|
|
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
|
@@ -0,0 +1,81 @@
|
|
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
|
@@ -4,12 +4,10 @@ module JSONAPI
|
|
4
4
|
class Index < Action
|
5
5
|
attr_accessor :resources
|
6
6
|
|
7
|
-
def initialize(payload:, headers:, type:)
|
8
|
-
@payload = payload
|
9
|
-
@headers = headers
|
7
|
+
def initialize(payload:, headers:, scope: nil, type:)
|
10
8
|
@type = type
|
11
9
|
|
12
|
-
super(payload: payload, headers: headers)
|
10
|
+
super(payload: payload, headers: headers, scope: scope)
|
13
11
|
|
14
12
|
@resources = adapter.find_many_via_call(relation).map(&resource_class.method(:new))
|
15
13
|
end
|
@@ -6,9 +6,36 @@ RSpec.describe JSONAPI::Realizer::Action::Index do
|
|
6
6
|
describe "#models" do
|
7
7
|
subject { action.models }
|
8
8
|
|
9
|
-
context "with no top-level data and good headers"
|
10
|
-
|
11
|
-
|
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
|
12
39
|
|
13
40
|
context "with a good payload and good headers" do
|
14
41
|
let(:payload) do
|
@@ -2,15 +2,12 @@ module JSONAPI
|
|
2
2
|
module Realizer
|
3
3
|
class Action
|
4
4
|
class Show < Action
|
5
|
-
|
6
5
|
attr_accessor :resource
|
7
6
|
|
8
|
-
def initialize(payload:, headers:, type:)
|
9
|
-
@payload = payload
|
10
|
-
@headers = headers
|
7
|
+
def initialize(payload:, headers:, scope: nil, type:)
|
11
8
|
@type = type
|
12
9
|
|
13
|
-
super(payload: payload, headers: headers)
|
10
|
+
super(payload: payload, headers: headers, scope: scope)
|
14
11
|
|
15
12
|
@resource = resource_class.new(adapter.find_via_call(relation, id))
|
16
13
|
end
|
@@ -6,9 +6,36 @@ RSpec.describe JSONAPI::Realizer::Action::Show do
|
|
6
6
|
describe "#model" do
|
7
7
|
subject { action.model }
|
8
8
|
|
9
|
-
context "with no top-level data and good headers"
|
10
|
-
|
11
|
-
|
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
|
12
39
|
|
13
40
|
context "with a good payload and good headers" do
|
14
41
|
let(:payload) do
|
@@ -4,26 +4,22 @@ module JSONAPI
|
|
4
4
|
class Update < Action
|
5
5
|
attr_accessor :resource
|
6
6
|
|
7
|
-
def initialize(payload:, headers:)
|
8
|
-
@payload = payload
|
9
|
-
@headers = headers
|
10
|
-
|
7
|
+
def initialize(payload:, headers:, scope: nil)
|
11
8
|
raise Error::MissingContentTypeHeader unless headers.key?("Content-Type")
|
12
|
-
raise Error::InvalidContentTypeHeader unless headers.fetch("Content-Type") ==
|
9
|
+
raise Error::InvalidContentTypeHeader unless headers.fetch("Content-Type") == JSONAPI::MEDIA_TYPE
|
13
10
|
|
14
|
-
super(payload: payload, headers: headers)
|
11
|
+
super(payload: payload, headers: headers, scope: scope)
|
15
12
|
|
16
13
|
@resource = resource_class.new(adapter.find_via_call(relation, id))
|
17
14
|
|
18
|
-
raise Error::MissingRootProperty unless payload.key?("data") || payload.key?("errors") || payload.key?("meta")
|
19
|
-
raise Error::MissingTypeResourceProperty if payload.key?("data") && data.kind_of?(Hash) && !data.key?("type")
|
20
|
-
raise Error::MissingTypeResourceProperty if payload.key?("data") && data.kind_of?(Array) && !data.all? {|resource| resource.key?("type")}
|
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")}
|
21
18
|
end
|
22
19
|
|
23
20
|
def call
|
24
21
|
adapter.assign_attributes_via_call(resource.model, attributes)
|
25
22
|
adapter.assign_relationships_via_call(resource.model, relationships)
|
26
|
-
adapter.update_via_call(resource.model)
|
27
23
|
end
|
28
24
|
|
29
25
|
def model
|
@@ -4,7 +4,7 @@ RSpec.describe JSONAPI::Realizer::Action::Update do
|
|
4
4
|
let(:action) { described_class.new(payload: payload, headers: headers) }
|
5
5
|
|
6
6
|
describe "#call" do
|
7
|
-
subject { action.call }
|
7
|
+
subject { action.tap(&:call) }
|
8
8
|
|
9
9
|
context "with no top-level data and no content-type header no accept headers" do
|
10
10
|
let(:payload) do
|
@@ -134,12 +134,6 @@ RSpec.describe JSONAPI::Realizer::Action::Update do
|
|
134
134
|
expect(action.model).to have_attributes(src: "http://example.com/images/productivity-2.png")
|
135
135
|
end
|
136
136
|
|
137
|
-
it "assigns the updated_at attribute" do
|
138
|
-
subject
|
139
|
-
|
140
|
-
expect(action.model).to have_attributes(updated_at: a_kind_of(Time))
|
141
|
-
end
|
142
|
-
|
143
137
|
it "assigns the active_photographer attribute" do
|
144
138
|
subject
|
145
139
|
|
@@ -163,27 +157,11 @@ RSpec.describe JSONAPI::Realizer::Action::Update do
|
|
163
157
|
include_examples "api"
|
164
158
|
|
165
159
|
it "updates the record" do
|
166
|
-
expect
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
{
|
172
|
-
"550e8400-e29b-41d4-a716-446655440000" => hash_including(
|
173
|
-
id: "550e8400-e29b-41d4-a716-446655440000",
|
174
|
-
title: "Ember Hamster",
|
175
|
-
src: "http://example.com/images/productivity.png"
|
176
|
-
)
|
177
|
-
}
|
178
|
-
).to(
|
179
|
-
{
|
180
|
-
"550e8400-e29b-41d4-a716-446655440000" => hash_including(
|
181
|
-
id: "550e8400-e29b-41d4-a716-446655440000",
|
182
|
-
title: "Ember Hamster 2",
|
183
|
-
alt_text: "A hamster logo.",
|
184
|
-
src: "http://example.com/images/productivity-2.png"
|
185
|
-
)
|
186
|
-
}
|
160
|
+
expect(subject.model).to have_attributes(
|
161
|
+
id: "550e8400-e29b-41d4-a716-446655440000",
|
162
|
+
title: "Ember Hamster 2",
|
163
|
+
alt_text: "A hamster logo.",
|
164
|
+
src: "http://example.com/images/productivity-2.png"
|
187
165
|
)
|
188
166
|
end
|
189
167
|
end
|
@@ -5,62 +5,66 @@ module JSONAPI
|
|
5
5
|
require_relative "action/update"
|
6
6
|
require_relative "action/show"
|
7
7
|
require_relative "action/index"
|
8
|
+
require_relative "action/destroy"
|
8
9
|
|
9
10
|
attr_reader :payload
|
10
11
|
attr_reader :headers
|
11
12
|
|
12
|
-
def initialize(payload:, headers:)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
raise Error::
|
13
|
+
def initialize(payload:, headers:, scope: nil)
|
14
|
+
@scope = scope
|
15
|
+
@headers = headers
|
16
|
+
@payload = payload
|
17
|
+
|
18
|
+
raise Error::MissingAcceptHeader unless @headers.key?("Accept")
|
19
|
+
raise Error::InvalidAcceptHeader unless @headers.fetch("Accept") == JSONAPI::MEDIA_TYPE
|
20
|
+
raise Error::IncludeWithoutDataProperty if @payload.key?("include") && !@payload.key?("data")
|
21
|
+
raise Error::MalformedDataRootProperty if @payload.key?("data") && !(data.kind_of?(Array) || data.kind_of?(Hash) || data.nil?)
|
18
22
|
end
|
19
23
|
|
20
24
|
def call; end
|
21
25
|
|
22
26
|
private def model_class
|
23
|
-
resource_class.model_class
|
27
|
+
resource_class.model_class if resource_class
|
24
28
|
end
|
25
29
|
|
26
30
|
private def resource_class
|
27
|
-
configuration.resource_class
|
31
|
+
configuration.resource_class if configuration
|
28
32
|
end
|
29
33
|
|
30
34
|
private def adapter
|
31
|
-
configuration.adapter
|
35
|
+
configuration.adapter if configuration
|
32
36
|
end
|
33
37
|
|
34
|
-
private def relation_after_inclusion(
|
38
|
+
private def relation_after_inclusion(subrelation)
|
35
39
|
if includes.any?
|
36
|
-
resource_class.include_via_call(
|
40
|
+
resource_class.include_via_call(subrelation, includes)
|
37
41
|
else
|
38
|
-
|
42
|
+
subrelation
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
|
-
private def relation_after_fields(
|
46
|
+
private def relation_after_fields(subrelation)
|
43
47
|
if fields.any?
|
44
|
-
resource_class.sparse_fields_call(
|
48
|
+
resource_class.sparse_fields_call(subrelation, fields)
|
45
49
|
else
|
46
|
-
|
50
|
+
subrelation
|
47
51
|
end
|
48
52
|
end
|
49
53
|
|
50
54
|
private def relation
|
51
55
|
relation_after_fields(
|
52
56
|
relation_after_inclusion(
|
53
|
-
model_class
|
57
|
+
@scope || model_class
|
54
58
|
)
|
55
59
|
)
|
56
60
|
end
|
57
61
|
|
58
62
|
private def data
|
59
|
-
payload.fetch("data")
|
63
|
+
payload.fetch("data", nil)
|
60
64
|
end
|
61
65
|
|
62
66
|
private def type
|
63
|
-
data["type"].to_s.dasherize if data
|
67
|
+
(@type || data["type"]).to_s.dasherize if @type || data
|
64
68
|
end
|
65
69
|
|
66
70
|
private def attributes
|
@@ -88,6 +92,7 @@ module JSONAPI
|
|
88
92
|
end
|
89
93
|
|
90
94
|
def includes
|
95
|
+
return [] unless payload.present?
|
91
96
|
return [] unless payload.key?("include")
|
92
97
|
|
93
98
|
payload.
|
@@ -111,6 +116,7 @@ module JSONAPI
|
|
111
116
|
end
|
112
117
|
|
113
118
|
def fields
|
119
|
+
return [] unless payload.present?
|
114
120
|
return [] unless payload.key?("fields")
|
115
121
|
|
116
122
|
payload.
|
@@ -138,7 +144,7 @@ module JSONAPI
|
|
138
144
|
end
|
139
145
|
|
140
146
|
private def configuration
|
141
|
-
JSONAPI::Realizer.type_mapping.fetch(type)
|
147
|
+
JSONAPI::Realizer.type_mapping.fetch(type) if type
|
142
148
|
end
|
143
149
|
end
|
144
150
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
RSpec.describe JSONAPI::Realizer::Action do
|
4
|
+
let(:headers) do
|
5
|
+
{
|
6
|
+
"Accept" => JSONAPI::MEDIA_TYPE
|
7
|
+
}
|
8
|
+
end
|
4
9
|
let(:action) do
|
5
|
-
|
6
|
-
def initialize(payload:, type:)
|
7
|
-
@payload = payload
|
8
|
-
@type = type
|
9
|
-
end
|
10
|
-
end.new(payload: payload, type: :photos)
|
10
|
+
ExampleAction.new(payload: payload, headers: headers, type: :photos)
|
11
11
|
end
|
12
12
|
|
13
13
|
describe "#includes" do
|
@@ -16,6 +16,7 @@ RSpec.describe JSONAPI::Realizer::Action do
|
|
16
16
|
context "with a two good and one bad" do
|
17
17
|
let(:payload) do
|
18
18
|
{
|
19
|
+
"data" => nil,
|
19
20
|
"include" => "active_photographer,active_photographer.posts.comments,active_photographer.posts"
|
20
21
|
}
|
21
22
|
end
|
@@ -32,6 +33,7 @@ RSpec.describe JSONAPI::Realizer::Action do
|
|
32
33
|
context "with a two good and one bad" do
|
33
34
|
let(:payload) do
|
34
35
|
{
|
36
|
+
"data" => nil,
|
35
37
|
"fields" => "title,active_photographer.posts.comments.body,active_photographer.name"
|
36
38
|
}
|
37
39
|
end
|
@@ -25,21 +25,6 @@ module JSONAPI
|
|
25
25
|
include_via do |model_class, includes|
|
26
26
|
model_class
|
27
27
|
end
|
28
|
-
|
29
|
-
create_via do |model|
|
30
|
-
model.assign_attributes(id: model.id || SecureRandom.uuid)
|
31
|
-
model.assign_attributes(updated_at: Time.now)
|
32
|
-
model.class.const_get("STORE")[model.id] = model.class.const_get("ATTRIBUTES").inject({}) do |hash, key|
|
33
|
-
hash.merge({ key => model.public_send(key) })
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
update_via do |model|
|
38
|
-
model.assign_attributes(updated_at: Time.now)
|
39
|
-
model.class.const_get("STORE")[model.id] = model.class.const_get("ATTRIBUTES").inject({}) do |hash, key|
|
40
|
-
hash.merge({ key => model.public_send(key) })
|
41
|
-
end
|
42
|
-
end
|
43
28
|
end
|
44
29
|
end
|
45
30
|
end
|
@@ -20,8 +20,6 @@ module JSONAPI
|
|
20
20
|
raise ArgumentError, "need to provide a Adapter.find_many_via_call interface" unless instance_variable_defined?(:@find_many_via_call)
|
21
21
|
raise ArgumentError, "need to provide a Adapter.assign_attributes_via interface" unless instance_variable_defined?(:@assign_attributes_via_call)
|
22
22
|
raise ArgumentError, "need to provide a Adapter.assign_relationships_via interface" unless instance_variable_defined?(:@assign_relationships_via_call)
|
23
|
-
raise ArgumentError, "need to provide a Adapter.create_via interface" unless instance_variable_defined?(:@create_via_call)
|
24
|
-
raise ArgumentError, "need to provide a Adapter.update_via interface" unless instance_variable_defined?(:@update_via_call)
|
25
23
|
raise ArgumentError, "need to provide a Adapter.sparse_fields interface" unless instance_variable_defined?(:@sparse_fields_call)
|
26
24
|
raise ArgumentError, "need to provide a Adapter.include_via interface" unless instance_variable_defined?(:@include_via_call)
|
27
25
|
end
|
@@ -34,14 +32,6 @@ module JSONAPI
|
|
34
32
|
@find_many_via_call = callback
|
35
33
|
end
|
36
34
|
|
37
|
-
def create_via(&callback)
|
38
|
-
@create_via_call = callback
|
39
|
-
end
|
40
|
-
|
41
|
-
def update_via(&callback)
|
42
|
-
@update_via_call = callback
|
43
|
-
end
|
44
|
-
|
45
35
|
def assign_attributes_via(&callback)
|
46
36
|
@assign_attributes_via_call = callback
|
47
37
|
end
|
@@ -66,14 +56,6 @@ module JSONAPI
|
|
66
56
|
@find_many_via_call.call(model_class)
|
67
57
|
end
|
68
58
|
|
69
|
-
def create_via_call(model)
|
70
|
-
@create_via_call.call(model)
|
71
|
-
end
|
72
|
-
|
73
|
-
def update_via_call(model)
|
74
|
-
@update_via_call.call(model)
|
75
|
-
end
|
76
|
-
|
77
59
|
def assign_attributes_via_call(model, attributes)
|
78
60
|
@assign_attributes_via_call.call(model, attributes)
|
79
61
|
end
|
@@ -26,13 +26,5 @@ RSpec.describe JSONAPI::Realizer::Adapter do
|
|
26
26
|
context "when the assign_attributes_via interface isn't defined" do
|
27
27
|
|
28
28
|
end
|
29
|
-
|
30
|
-
context "when the create_via interface isn't defined" do
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
context "when the update_via interface isn't defined" do
|
35
|
-
|
36
|
-
end
|
37
29
|
end
|
38
30
|
end
|
data/lib/jsonapi/realizer.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-realizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kurtis Rainbolt-Greene
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -136,6 +136,8 @@ files:
|
|
136
136
|
- lib/jsonapi/realizer/action.rb
|
137
137
|
- lib/jsonapi/realizer/action/create.rb
|
138
138
|
- lib/jsonapi/realizer/action/create_spec.rb
|
139
|
+
- lib/jsonapi/realizer/action/destroy.rb
|
140
|
+
- lib/jsonapi/realizer/action/destroy_spec.rb
|
139
141
|
- lib/jsonapi/realizer/action/index.rb
|
140
142
|
- lib/jsonapi/realizer/action/index_spec.rb
|
141
143
|
- lib/jsonapi/realizer/action/show.rb
|
@@ -156,7 +158,6 @@ files:
|
|
156
158
|
- lib/jsonapi/realizer/error/missing_content_type_header.rb
|
157
159
|
- lib/jsonapi/realizer/error/missing_root_property.rb
|
158
160
|
- lib/jsonapi/realizer/error/missing_type_resource_property.rb
|
159
|
-
- lib/jsonapi/realizer/error/too_many_root_properties.rb
|
160
161
|
- lib/jsonapi/realizer/resource.rb
|
161
162
|
- lib/jsonapi/realizer/version.rb
|
162
163
|
- lib/jsonapi/realizer/version_spec.rb
|
@@ -181,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
182
|
version: '0'
|
182
183
|
requirements: []
|
183
184
|
rubyforge_project:
|
184
|
-
rubygems_version: 2.7.
|
185
|
+
rubygems_version: 2.7.6
|
185
186
|
signing_key:
|
186
187
|
specification_version: 4
|
187
188
|
summary: A way to take json:api requests and turn them into models
|