jsonapi-realizer 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|