jsonapi_spec_helpers 0.4.10 → 1.0.alpha.2
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/jsonapi_spec_helpers.gemspec +1 -0
- data/lib/jsonapi_spec_helpers/errors.rb +42 -7
- data/lib/jsonapi_spec_helpers/errors_proxy.rb +70 -0
- data/lib/jsonapi_spec_helpers/helpers.rb +53 -63
- data/lib/jsonapi_spec_helpers/node.rb +81 -0
- data/lib/jsonapi_spec_helpers/rspec.rb +44 -0
- data/lib/jsonapi_spec_helpers/version.rb +1 -1
- data/lib/jsonapi_spec_helpers.rb +18 -39
- metadata +22 -8
- data/lib/jsonapi_spec_helpers/matchers.rb +0 -61
- data/lib/jsonapi_spec_helpers/payload.rb +0 -66
- data/lib/jsonapi_spec_helpers/payload_sanitizer.rb +0 -93
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17ecff418f690fa93ed1e4a44d139c46a9b00beb
|
4
|
+
data.tar.gz: 4e74adb0ea0593f7a28e7bc7485f20e6d55a1d08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58f02d6265d70de7e97c544646698e71852421ae213f53e74f5e4bb187ffafb6fb29fabb63929906fbfea8a8d7f5a4cd6996a1514ee620a6e38e23e99903582c
|
7
|
+
data.tar.gz: c216961f4a62b64fb0a42058c1a3fcab986a9c1e4b43a2a7f1da61f8981b90bceb4eeb4b9f57263d902f62f78caae97f3ef529706893f6ed27d851a420237ecd
|
@@ -23,5 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency "actionpack", "~> 5.0"
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.11"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "jsonapi_compliable", '>= 1.0.alpha.1'
|
26
27
|
spec.add_dependency "rspec", "~> 3.0"
|
27
28
|
end
|
@@ -1,16 +1,51 @@
|
|
1
1
|
module JsonapiSpecHelpers
|
2
2
|
module Errors
|
3
3
|
class Base < StandardError; end
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
class LinksNotFound < Base
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def message
|
11
|
+
"Relationship with name '#{@name}' has no links!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class SideloadNotFound < Base
|
16
|
+
def initialize(name)
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
"Relationship with name '#{@name}' not found!"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class NoResponse < Base
|
26
|
+
def message
|
27
|
+
"Cannot parse response - missing #response.body!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class NoData < Base
|
32
|
+
def initialize(payload)
|
33
|
+
@payload = payload
|
34
|
+
end
|
35
|
+
|
36
|
+
def message
|
37
|
+
"Payload did not contain 'data'! Payload was:\n\n#{JSON.pretty_generate(@payload)}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class NoSideloads < Base
|
42
|
+
def initialize(payload)
|
43
|
+
@payload = payload
|
7
44
|
end
|
8
45
|
|
9
46
|
def message
|
10
|
-
"
|
11
|
-
"from the included property of your JSON payload. But it contained " \
|
12
|
-
"#{@array.length} '#{@type}'"
|
47
|
+
"Tried to find sideload, but the payload did not contain 'included'! Payload was:\n\n#{JSON.pretty_generate(@payload)}"
|
13
48
|
end
|
14
49
|
end
|
15
50
|
end
|
16
|
-
end
|
51
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module JsonapiSpecHelpers
|
2
|
+
class ErrorsProxy
|
3
|
+
class Error
|
4
|
+
attr_reader :json
|
5
|
+
|
6
|
+
def initialize(json)
|
7
|
+
@json = json
|
8
|
+
end
|
9
|
+
|
10
|
+
def attribute
|
11
|
+
@json[:meta][:attribute]
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO: move to top-level code in errorable
|
15
|
+
def code
|
16
|
+
@json[:meta][:code]
|
17
|
+
end
|
18
|
+
|
19
|
+
def message
|
20
|
+
@json[:meta][:message]
|
21
|
+
end
|
22
|
+
|
23
|
+
def title
|
24
|
+
@json[:title]
|
25
|
+
end
|
26
|
+
|
27
|
+
def detail
|
28
|
+
@json[:detail]
|
29
|
+
end
|
30
|
+
|
31
|
+
def status
|
32
|
+
@json[:status]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
include Enumerable
|
37
|
+
|
38
|
+
def initialize(array)
|
39
|
+
@errors = array.map { |e| Error.new(e) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def [](key)
|
43
|
+
@errors[key]
|
44
|
+
end
|
45
|
+
|
46
|
+
def each(&blk)
|
47
|
+
@errors.each(&blk)
|
48
|
+
end
|
49
|
+
|
50
|
+
def length
|
51
|
+
count
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_h
|
55
|
+
{}.tap do |hash|
|
56
|
+
@errors.each do |e|
|
57
|
+
hash[e.attribute] = e.message
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def method_missing(id, *args, &blk)
|
63
|
+
if error = @errors.find { |e| e.attribute == id }
|
64
|
+
return error
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,77 +1,40 @@
|
|
1
1
|
module JsonapiSpecHelpers
|
2
2
|
module Helpers
|
3
|
-
|
4
|
-
JSON.parse(response.body)
|
5
|
-
end
|
6
|
-
|
7
|
-
def json_item(from: nil)
|
8
|
-
from = json if from.nil?
|
9
|
-
data = from.has_key?('data') ? from['data'] : from
|
10
|
-
|
11
|
-
{}.tap do |item|
|
12
|
-
item['id'] = data['id']
|
13
|
-
item['jsonapi_type'] = data['type']
|
14
|
-
item.merge!(data['attributes']) if data.has_key?('attributes')
|
15
|
-
end
|
16
|
-
end
|
3
|
+
extend ActiveSupport::Concern
|
17
4
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
5
|
+
def json
|
6
|
+
if response && response.body
|
7
|
+
JSON.parse(response.body).with_indifferent_access
|
8
|
+
else
|
9
|
+
raise Errors::NoResponse.new
|
23
10
|
end
|
24
|
-
indices.length == 1 ? items[0] : items
|
25
|
-
end
|
26
|
-
|
27
|
-
def json_related_link(payload, assn_name)
|
28
|
-
link = payload['relationships'][assn_name]['links']['related']['href']
|
29
|
-
fail "link for #{assn_name} not found" unless link
|
30
|
-
URI.decode(link)
|
31
11
|
end
|
32
12
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
indices = (0...included.length).to_a if indices.empty?
|
40
|
-
includes = []
|
41
|
-
indices.each do |index|
|
42
|
-
single_included = included.at(index)
|
43
|
-
if single_included.nil?
|
44
|
-
raise Errors::IncludedOutOfBounds.new(type, index, included)
|
13
|
+
def jsonapi_data
|
14
|
+
@jsonapi_data ||= begin
|
15
|
+
if _jsonapi_data.is_a?(Hash)
|
16
|
+
node(from: _jsonapi_data)
|
17
|
+
else
|
18
|
+
_jsonapi_data.map { |datum| node(from: datum) }
|
45
19
|
end
|
46
|
-
includes << json_item(from: single_included)
|
47
20
|
end
|
48
|
-
includes
|
49
21
|
end
|
50
22
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
23
|
+
def jsonapi_included(type = nil)
|
24
|
+
variable = :"@jsonapi_included#{type}"
|
25
|
+
memo = instance_variable_get(variable)
|
26
|
+
return memo if memo
|
54
27
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
28
|
+
nodes = _jsonapi_included.map { |i| node(from: i) }
|
29
|
+
if type
|
30
|
+
nodes.select! { |n| n.jsonapi_type == type }
|
31
|
+
end
|
32
|
+
instance_variable_set(variable, nodes)
|
33
|
+
nodes
|
59
34
|
end
|
60
35
|
|
61
|
-
def
|
62
|
-
@
|
63
|
-
return errors if json['errors'].nil?
|
64
|
-
json['errors'].each do |e|
|
65
|
-
attr = e['meta']['attribute'].to_sym
|
66
|
-
message = e['meta']['message']
|
67
|
-
|
68
|
-
if errors[attr]
|
69
|
-
errors[attr] = Array(errors[attr]).push(message)
|
70
|
-
else
|
71
|
-
errors[attr] = message
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
36
|
+
def jsonapi_errors
|
37
|
+
@jsonapi_errors ||= ErrorsProxy.new(json['errors'] || [])
|
75
38
|
end
|
76
39
|
|
77
40
|
def jsonapi_headers
|
@@ -100,8 +63,35 @@ module JsonapiSpecHelpers
|
|
100
63
|
delete url, headers: jsonapi_headers
|
101
64
|
end
|
102
65
|
|
103
|
-
def
|
104
|
-
|
66
|
+
def datetime(value)
|
67
|
+
JsonapiCompliable::Types[:datetime][:read][value]
|
68
|
+
end
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
def node(from: nil)
|
72
|
+
from = json if from.nil?
|
73
|
+
data = from.has_key?('data') ? from['data'] : from
|
74
|
+
hash = {}
|
75
|
+
hash['id'] = data['id']
|
76
|
+
hash['jsonapi_type'] = data['type']
|
77
|
+
hash.merge!(data['attributes']) if data.has_key?('attributes')
|
78
|
+
Node.new(hash, data['relationships'], self)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# @api private
|
84
|
+
def _jsonapi_data
|
85
|
+
json['data'] || raise(Errors::NoData.new(json))
|
86
|
+
end
|
87
|
+
|
88
|
+
# @api private
|
89
|
+
def _jsonapi_included
|
90
|
+
if json.has_key?('included')
|
91
|
+
json['included']
|
92
|
+
else
|
93
|
+
raise Errors::NoSideloads.new(json)
|
94
|
+
end
|
105
95
|
end
|
106
96
|
end
|
107
97
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module JsonapiSpecHelpers
|
2
|
+
class Node
|
3
|
+
attr_reader :attributes, :relationships
|
4
|
+
|
5
|
+
def initialize(attributes, relationships, context)
|
6
|
+
@attributes = attributes.with_indifferent_access
|
7
|
+
@relationships = relationships
|
8
|
+
@context = context
|
9
|
+
end
|
10
|
+
|
11
|
+
def id
|
12
|
+
rawid.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def rawid
|
16
|
+
@attributes['id']
|
17
|
+
end
|
18
|
+
|
19
|
+
def jsonapi_type
|
20
|
+
@attributes['jsonapi_type']
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_key?(key)
|
24
|
+
@attributes.has_key?(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](key)
|
28
|
+
@attributes[key] || @attributes[key.to_s]
|
29
|
+
end
|
30
|
+
|
31
|
+
def []=(key, val)
|
32
|
+
@attributes[key] = val
|
33
|
+
end
|
34
|
+
|
35
|
+
def attributes
|
36
|
+
@attributes
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(id, *args, &blk)
|
40
|
+
if @attributes.has_key?(id)
|
41
|
+
@attributes[id]
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def link(relationship_name, name)
|
48
|
+
if @relationships.has_key?(relationship_name)
|
49
|
+
links = @relationships[relationship_name][:links]
|
50
|
+
raise Errors::LinksNotFound.new(relationship_name) unless links
|
51
|
+
links[name]
|
52
|
+
else
|
53
|
+
raise Errors::SideloadNotFound.new(relationship_name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def sideload(relationship_name)
|
58
|
+
unless @relationships.has_key?(relationship_name)
|
59
|
+
raise Errors::SideloadNotFound.new(relationship_name)
|
60
|
+
end
|
61
|
+
rel = @relationships[relationship_name]
|
62
|
+
rel = rel[:data]
|
63
|
+
return if rel.nil?
|
64
|
+
if rel.is_a?(Hash)
|
65
|
+
include_for(rel[:type], rel[:id])
|
66
|
+
else
|
67
|
+
rel.map { |r| include_for(r[:type], r[:id]) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
alias :sideloads :sideload
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def include_for(type, id)
|
75
|
+
data = @context.json[:included].find do |i|
|
76
|
+
i[:type] == type && i[:id] == id
|
77
|
+
end
|
78
|
+
@context.node(from: data)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'jsonapi_spec_helpers'
|
2
|
+
|
3
|
+
::RSpec.shared_context 'resource testing', type: :resource do |parameter|
|
4
|
+
let(:resource) { described_class }
|
5
|
+
let(:params) { {} }
|
6
|
+
|
7
|
+
# If you need to set context:
|
8
|
+
#
|
9
|
+
# JsonapiCompliable.with_context my_context, {} do
|
10
|
+
# render
|
11
|
+
# end
|
12
|
+
def render(runtime_options = {})
|
13
|
+
json = proxy.to_jsonapi(runtime_options)
|
14
|
+
response.body = json
|
15
|
+
json
|
16
|
+
end
|
17
|
+
|
18
|
+
def proxy
|
19
|
+
@proxy ||= begin
|
20
|
+
ctx = ::JsonapiSpecHelpers::TestRunner.new(resource, params)
|
21
|
+
defined?(base_scope) ? ctx.proxy(base_scope) : ctx.proxy
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def records
|
26
|
+
proxy.data
|
27
|
+
end
|
28
|
+
|
29
|
+
def response
|
30
|
+
@response ||= OpenStruct.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module JsonapiSpecHelpers
|
35
|
+
module RSpec
|
36
|
+
def self.included(klass)
|
37
|
+
klass.send(:include, JsonapiSpecHelpers)
|
38
|
+
|
39
|
+
::RSpec.configure do |rspec|
|
40
|
+
rspec.include_context "resource testing", type: :resource
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/jsonapi_spec_helpers.rb
CHANGED
@@ -1,58 +1,37 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'pp'
|
3
|
+
require 'active_support/core_ext/string'
|
4
|
+
require 'active_support/core_ext/hash'
|
5
|
+
require 'jsonapi_compliable'
|
6
|
+
|
2
7
|
require 'jsonapi_spec_helpers/version'
|
3
8
|
require 'jsonapi_spec_helpers/helpers'
|
4
|
-
require 'jsonapi_spec_helpers/
|
5
|
-
require 'jsonapi_spec_helpers/
|
9
|
+
require 'jsonapi_spec_helpers/node'
|
10
|
+
require 'jsonapi_spec_helpers/errors_proxy'
|
6
11
|
require 'jsonapi_spec_helpers/errors'
|
7
12
|
|
8
13
|
module JsonapiSpecHelpers
|
9
14
|
def self.included(klass)
|
10
|
-
# don't load RSpec until included
|
11
|
-
require 'jsonapi_spec_helpers/matchers'
|
12
15
|
klass.send(:include, Helpers)
|
13
|
-
if defined?(Rails)
|
14
|
-
load_payloads!
|
15
|
-
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
class TestRunner < ::JsonapiCompliable::Runner
|
19
|
+
def current_user
|
20
|
+
nil
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
module Sugar
|
25
|
+
def d
|
26
|
+
jsonapi_data
|
25
27
|
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
payload.instance_eval(&blk)
|
29
|
+
def included(type = nil)
|
30
|
+
jsonapi_included(type)
|
30
31
|
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
prc = options[:proc]
|
35
|
-
if (expect(json).to have_payload_key(attribute, options[:allow_nil])) == true
|
36
|
-
unless options[:allow_nil]
|
37
|
-
output = instance_exec(record, &prc)
|
38
|
-
expect(json[attribute.to_s]).to match_payload(attribute, output)
|
39
|
-
|
40
|
-
if options[:type]
|
41
|
-
expect(json[attribute.to_s]).to match_type(attribute, options[:type])
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
payload.no_keys.each do |no_key|
|
48
|
-
expect(json).to_not have_payload_key(no_key, {})
|
49
|
-
end
|
50
|
-
|
51
|
-
unexpected_keys = json.keys - payload.keys.keys.map(&:to_s)
|
52
|
-
unexpected_keys.reject! { |k| %w(id jsonapi_type).include?(k) }
|
53
|
-
unexpected_keys.each do |key|
|
54
|
-
expect(key).to be_not_in_payload
|
55
|
-
end
|
33
|
+
def errors
|
34
|
+
jsonapi_errors
|
56
35
|
end
|
57
36
|
end
|
58
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi_spec_helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.alpha.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: jsonapi_compliable
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.0.alpha.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.0.alpha.1
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rspec
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,10 +128,10 @@ files:
|
|
114
128
|
- jsonapi_spec_helpers.gemspec
|
115
129
|
- lib/jsonapi_spec_helpers.rb
|
116
130
|
- lib/jsonapi_spec_helpers/errors.rb
|
131
|
+
- lib/jsonapi_spec_helpers/errors_proxy.rb
|
117
132
|
- lib/jsonapi_spec_helpers/helpers.rb
|
118
|
-
- lib/jsonapi_spec_helpers/
|
119
|
-
- lib/jsonapi_spec_helpers/
|
120
|
-
- lib/jsonapi_spec_helpers/payload_sanitizer.rb
|
133
|
+
- lib/jsonapi_spec_helpers/node.rb
|
134
|
+
- lib/jsonapi_spec_helpers/rspec.rb
|
121
135
|
- lib/jsonapi_spec_helpers/version.rb
|
122
136
|
homepage:
|
123
137
|
licenses:
|
@@ -134,12 +148,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
148
|
version: '0'
|
135
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
150
|
requirements:
|
137
|
-
- - "
|
151
|
+
- - ">"
|
138
152
|
- !ruby/object:Gem::Version
|
139
|
-
version:
|
153
|
+
version: 1.3.1
|
140
154
|
requirements: []
|
141
155
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.6.
|
156
|
+
rubygems_version: 2.6.13
|
143
157
|
signing_key:
|
144
158
|
specification_version: 4
|
145
159
|
summary: Spec helpers for jsonapi
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'rspec/matchers'
|
2
|
-
|
3
|
-
RSpec::Matchers.define :match_payload do |attribute, expected|
|
4
|
-
match do |actual|
|
5
|
-
if expected.respond_to?(:as_json)
|
6
|
-
expected = expected.as_json
|
7
|
-
end
|
8
|
-
|
9
|
-
actual == expected
|
10
|
-
end
|
11
|
-
|
12
|
-
failure_message do |actual|
|
13
|
-
"Expected JSON payload to have key '#{attribute}' == #{expected.inspect} but was #{actual.inspect}"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
RSpec::Matchers.define :match_type do |attribute, type|
|
18
|
-
match do |actual|
|
19
|
-
if type.is_a?(Array)
|
20
|
-
type.any? { |t| actual.is_a?(t) }
|
21
|
-
else
|
22
|
-
actual.is_a?(type)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
failure_message do |actual|
|
27
|
-
"Expected JSON payload key '#{attribute}' to have type #{type} but was #{actual.class}"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
RSpec::Matchers.define :have_payload_key do |expected, allow_nil|
|
32
|
-
match do |json|
|
33
|
-
@has_key = json.has_key?(expected.to_s)
|
34
|
-
@has_value = !json[expected.to_s].nil?
|
35
|
-
|
36
|
-
if allow_nil
|
37
|
-
@has_key
|
38
|
-
else
|
39
|
-
@has_key && @has_value
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
failure_message do |actual|
|
44
|
-
msg = !allow_nil && @has_key ? "nil. Use 'key(:foo, allow_nil: true)' to allow nils" : "not present"
|
45
|
-
"Expected JSON payload to have key '#{expected}' but was #{msg}"
|
46
|
-
end
|
47
|
-
|
48
|
-
failure_message_when_negated do |actual|
|
49
|
-
"Expected JSON payload to NOT have key '#{expected}' but was present"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
RSpec::Matchers.define :be_not_in_payload do |expected|
|
54
|
-
match do |json|
|
55
|
-
false
|
56
|
-
end
|
57
|
-
|
58
|
-
failure_message do |actual|
|
59
|
-
"JSON payload contained unexpected key '#{actual}'"
|
60
|
-
end
|
61
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
module JsonapiSpecHelpers
|
2
|
-
class Payload
|
3
|
-
class << self
|
4
|
-
attr_accessor :registry
|
5
|
-
end
|
6
|
-
self.registry = {}
|
7
|
-
|
8
|
-
attr_accessor :name, :type, :keys, :no_keys
|
9
|
-
|
10
|
-
def self.register(name, &blk)
|
11
|
-
instance = new
|
12
|
-
instance.instance_eval(&blk)
|
13
|
-
instance.name = name
|
14
|
-
registry[name] = instance
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.by_type(type)
|
18
|
-
found = nil
|
19
|
-
registry.each_pair do |name, payload|
|
20
|
-
found = payload if payload.type == type
|
21
|
-
end
|
22
|
-
raise "Could not find payload for type #{type}" unless found
|
23
|
-
found
|
24
|
-
end
|
25
|
-
|
26
|
-
def fork
|
27
|
-
instance = self.class.new
|
28
|
-
instance.keys = keys.dup
|
29
|
-
instance.no_keys = no_keys.dup
|
30
|
-
instance
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@keys = {}
|
35
|
-
@no_keys = []
|
36
|
-
end
|
37
|
-
|
38
|
-
def no_key(name)
|
39
|
-
@keys.delete(name)
|
40
|
-
@no_keys << name
|
41
|
-
end
|
42
|
-
|
43
|
-
def type(val = nil)
|
44
|
-
if val
|
45
|
-
@type = val
|
46
|
-
else
|
47
|
-
@type || name.to_s.pluralize.to_sym
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def key(name, *args, &blk)
|
52
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
53
|
-
options[:type] = args.first
|
54
|
-
options[:allow_nil] ||= false
|
55
|
-
@no_keys.reject! { |k| k == name }
|
56
|
-
prc = blk
|
57
|
-
prc = ->(record) { record.send(name) } if prc.nil?
|
58
|
-
@keys[name] = options.merge(proc: prc)
|
59
|
-
end
|
60
|
-
|
61
|
-
def timestamps!
|
62
|
-
@keys[:created_at] = key(:created_at, String)
|
63
|
-
@keys[:updated_at] = key(:updated_at, String)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
module JsonapiSpecHelpers
|
2
|
-
class PayloadSanitizer
|
3
|
-
def initialize(payload)
|
4
|
-
@payload = payload
|
5
|
-
@included = []
|
6
|
-
end
|
7
|
-
|
8
|
-
def resource
|
9
|
-
@resource ||= sane_resource(@payload)
|
10
|
-
end
|
11
|
-
|
12
|
-
def sanitize
|
13
|
-
resource[:relationships].each_pair do |key, relationship_payload|
|
14
|
-
set_default_relationship_payload(key, relationship_payload)
|
15
|
-
|
16
|
-
if relationship_payload.is_a?(Array)
|
17
|
-
relationship_payload.each do |p|
|
18
|
-
process_relationship(key, p)
|
19
|
-
end
|
20
|
-
else
|
21
|
-
process_relationship(key, relationship_payload)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
payload = { data: data }
|
26
|
-
payload[:included] = @included
|
27
|
-
payload
|
28
|
-
end
|
29
|
-
|
30
|
-
def included
|
31
|
-
@included
|
32
|
-
end
|
33
|
-
|
34
|
-
def add_include(incl)
|
35
|
-
@included.push(incl) if @included.index(incl).nil?
|
36
|
-
end
|
37
|
-
|
38
|
-
def resource_identifier
|
39
|
-
{}.tap do |ri|
|
40
|
-
ri[:id] = resource[:id] if resource[:id]
|
41
|
-
ri[:type] = resource[:type]
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def data
|
46
|
-
@data ||= {}.tap do |d|
|
47
|
-
d[:id] = resource[:id] if resource[:id]
|
48
|
-
d[:type] = resource[:type]
|
49
|
-
d[:attributes] = resource[:attributes]
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def set_default_relationship_payload(key, payload)
|
56
|
-
data[:relationships] ||= {}
|
57
|
-
if payload.is_a?(Array)
|
58
|
-
data[:relationships][key] ||= { data: [] }
|
59
|
-
else
|
60
|
-
data[:relationships][key] ||= { data: nil }
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def process_relationship(name, relationship_payload)
|
65
|
-
sanitizer = self.class.new(relationship_payload)
|
66
|
-
|
67
|
-
if data[:relationships][name][:data].is_a?(Array)
|
68
|
-
data[:relationships][name][:data] << sanitizer.resource_identifier
|
69
|
-
else
|
70
|
-
data[:relationships][name] = { data: sanitizer.resource_identifier }
|
71
|
-
end
|
72
|
-
|
73
|
-
sanitized = sanitizer.sanitize
|
74
|
-
add_include(sanitized[:data])
|
75
|
-
sanitized[:included].each do |incl|
|
76
|
-
add_include(incl)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def sane_resource(payload)
|
81
|
-
id, type = payload.delete(:id), payload.delete(:type)
|
82
|
-
relationships = payload.delete(:relationships) || {}
|
83
|
-
raise 'jsonapi payloads must specify a "type"' if type.nil?
|
84
|
-
|
85
|
-
{
|
86
|
-
id: id,
|
87
|
-
type: type,
|
88
|
-
attributes: payload,
|
89
|
-
relationships: relationships
|
90
|
-
}
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|