fun_with_json_api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +28 -0
- data/config/locales/fun_with_json_api.en.yml +13 -0
- data/lib/fun_with_json_api/attribute.rb +38 -0
- data/lib/fun_with_json_api/attributes/boolean_attribute.rb +24 -0
- data/lib/fun_with_json_api/attributes/date_attribute.rb +22 -0
- data/lib/fun_with_json_api/attributes/datetime_attribute.rb +20 -0
- data/lib/fun_with_json_api/attributes/decimal_attribute.rb +23 -0
- data/lib/fun_with_json_api/attributes/float_attribute.rb +20 -0
- data/lib/fun_with_json_api/attributes/integer_attribute.rb +20 -0
- data/lib/fun_with_json_api/attributes/relationship.rb +73 -0
- data/lib/fun_with_json_api/attributes/relationship_collection.rb +99 -0
- data/lib/fun_with_json_api/attributes/string_attribute.rb +9 -0
- data/lib/fun_with_json_api/controller_methods.rb +12 -0
- data/lib/fun_with_json_api/deserializer.rb +70 -0
- data/lib/fun_with_json_api/deserializer_class_methods.rb +83 -0
- data/lib/fun_with_json_api/deserializer_config_builder.rb +48 -0
- data/lib/fun_with_json_api/exception.rb +27 -0
- data/lib/fun_with_json_api/exception_payload.rb +29 -0
- data/lib/fun_with_json_api/exception_payload_serializer.rb +17 -0
- data/lib/fun_with_json_api/exception_serializer.rb +15 -0
- data/lib/fun_with_json_api/exceptions/invalid_attribute.rb +13 -0
- data/lib/fun_with_json_api/exceptions/invalid_document.rb +12 -0
- data/lib/fun_with_json_api/exceptions/invalid_relationship.rb +13 -0
- data/lib/fun_with_json_api/exceptions/missing_relationship.rb +15 -0
- data/lib/fun_with_json_api/pre_deserializer.rb +61 -0
- data/lib/fun_with_json_api/railtie.rb +11 -0
- data/lib/fun_with_json_api/version.rb +3 -0
- data/lib/fun_with_json_api.rb +24 -0
- data/lib/tasks/fun_with_json_api_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config/application.rb +25 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/log/test.log +37839 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/example_spec.rb +64 -0
- data/spec/fixtures/active_record.rb +65 -0
- data/spec/fun_with_json_api/controller_methods_spec.rb +123 -0
- data/spec/fun_with_json_api/deserializer_class_methods_spec.rb +52 -0
- data/spec/fun_with_json_api/deserializer_spec.rb +450 -0
- data/spec/fun_with_json_api/exception_spec.rb +77 -0
- data/spec/fun_with_json_api/pre_deserializer_spec.rb +287 -0
- data/spec/fun_with_json_api_spec.rb +55 -0
- data/spec/spec_helper.rb +33 -0
- metadata +275 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 527661f615009228dc573ed949ccd8ea04349382
|
4
|
+
data.tar.gz: d93b9e012c63f7fab576f9c40c54af9bc3fd8c6e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59d7335c4ecb53d0b3b7800e6635ae52240d41750f626f443e567a7025b206245db1a362d13e6d5ee440b561c47e67b8acb7e0395ee511b9a039bf6f5177233f
|
7
|
+
data.tar.gz: 2daad3bbbb9ea0c93fc996bee1f0ded6dade569ef0223e60985a48b42c19aaa176ae4e4920146bb4855f0b344d19abe0508a9936ce06eccc4fa21106ef32455e
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Ben Morrall
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'FunWithJsonApi'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
Bundler::GemHelper.install_tasks
|
18
|
+
|
19
|
+
require 'rubocop/rake_task'
|
20
|
+
RuboCop::RakeTask.new
|
21
|
+
|
22
|
+
require 'rspec/core'
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
|
25
|
+
desc 'Run all specs in spec directory (excluding plugin specs)'
|
26
|
+
RSpec::Core::RakeTask.new(:spec)
|
27
|
+
|
28
|
+
task default: [:spec, :rubocop]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
en:
|
2
|
+
fun_with_json_api:
|
3
|
+
exceptions:
|
4
|
+
invalid_document: 'Request json_api document is invalid'
|
5
|
+
invalid_attribute: 'Request json_api document attribute is invalid'
|
6
|
+
invalid_relationship: 'Request json_api document relationship is invalid'
|
7
|
+
missing_relationship: 'Unable to find the requested relationship'
|
8
|
+
invalid_boolean_attribute: "Boolean value should only be true, false, or null"
|
9
|
+
invalid_date_attribute: "Date value should be in the format YYYY-MM-DD"
|
10
|
+
invalid_datetime_attribute: "Datetime value should be a ISO 8601 datetime"
|
11
|
+
invalid_decimal_attribute: "Decimal value must be a decimal number (i.e. 123.45)"
|
12
|
+
invalid_float_attribute: "Float value must be a floating point number (i.e. 123.45)"
|
13
|
+
invalid_integer_attribute: "Integer value must be a integer number (i.e. 123)"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
class Attribute
|
3
|
+
attr_reader :name
|
4
|
+
attr_reader :as
|
5
|
+
|
6
|
+
def self.create(name, options = {})
|
7
|
+
format = options.fetch(:format, 'string')
|
8
|
+
attribute_class_name = "#{format.to_s.classify}Attribute"
|
9
|
+
if FunWithJsonApi::Attributes.const_defined?(attribute_class_name)
|
10
|
+
FunWithJsonApi::Attributes.const_get(attribute_class_name)
|
11
|
+
else
|
12
|
+
raise ArgumentError, "Unknown attribute type: #{format}"
|
13
|
+
end.new(name, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(name, options = {})
|
17
|
+
raise ArgumentError, 'name cannot be blank!' unless name.present?
|
18
|
+
|
19
|
+
@name = name
|
20
|
+
@as = options.fetch(:as, name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(value)
|
24
|
+
value
|
25
|
+
end
|
26
|
+
|
27
|
+
def sanitize_attribute_method
|
28
|
+
:"parse_#{param_value}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def param_value
|
32
|
+
as.to_sym
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Load pre-defined Attributes
|
38
|
+
Dir["#{File.dirname(__FILE__)}/attributes/**/*.rb"].each { |f| require f }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
# Ensures a value is either Boolean.TRUE, Boolean.FALSE or nil
|
4
|
+
# Raises an argument error otherwise
|
5
|
+
class BooleanAttribute < Attribute
|
6
|
+
def call(value)
|
7
|
+
return nil if value.nil?
|
8
|
+
return value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
9
|
+
|
10
|
+
raise build_invalid_attribute_error(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def build_invalid_attribute_error(value)
|
16
|
+
exception_message = I18n.t('fun_with_json_api.exceptions.invalid_boolean_attribute')
|
17
|
+
payload = ExceptionPayload.new
|
18
|
+
payload.detail = exception_message
|
19
|
+
payload.pointer = "/data/attributes/#{name}"
|
20
|
+
Exceptions::InvalidAttribute.new(exception_message + ": #{value.inspect}", payload)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
class DateAttribute < Attribute
|
4
|
+
DATE_FORMAT = '%Y-%m-%d'.freeze
|
5
|
+
|
6
|
+
def call(value)
|
7
|
+
Date.strptime(value, DATE_FORMAT) if value
|
8
|
+
rescue ArgumentError => exception
|
9
|
+
raise build_invalid_attribute_error(exception, value)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def build_invalid_attribute_error(exception, value)
|
15
|
+
payload = ExceptionPayload.new
|
16
|
+
payload.detail = I18n.t('fun_with_json_api.exceptions.invalid_date_attribute')
|
17
|
+
payload.pointer = "/data/attributes/#{name}"
|
18
|
+
Exceptions::InvalidAttribute.new(exception.message + ": #{value.inspect}", payload)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
class DatetimeAttribute < Attribute
|
4
|
+
def call(value)
|
5
|
+
DateTime.iso8601(value) if value
|
6
|
+
rescue ArgumentError => exception
|
7
|
+
raise build_invalid_attribute_error(exception, value)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def build_invalid_attribute_error(exception, value)
|
13
|
+
payload = ExceptionPayload.new
|
14
|
+
payload.detail = I18n.t('fun_with_json_api.exceptions.invalid_datetime_attribute')
|
15
|
+
payload.pointer = "/data/attributes/#{name}"
|
16
|
+
Exceptions::InvalidAttribute.new(exception.message + ": #{value.inspect}", payload)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
class DecimalAttribute < Attribute
|
4
|
+
def call(value)
|
5
|
+
if value
|
6
|
+
unless value.to_s =~ /[0-9]+(\.[0-9]+)?/
|
7
|
+
raise build_invalid_attribute_error(value)
|
8
|
+
end
|
9
|
+
BigDecimal.new(value.to_s)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def build_invalid_attribute_error(value)
|
16
|
+
payload = ExceptionPayload.new
|
17
|
+
payload.detail = I18n.t('fun_with_json_api.exceptions.invalid_decimal_attribute')
|
18
|
+
payload.pointer = "/data/attributes/#{name}"
|
19
|
+
Exceptions::InvalidAttribute.new("Unable to parse decimal: #{value.inspect}", payload)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
class FloatAttribute < FunWithJsonApi::Attribute
|
4
|
+
def call(value)
|
5
|
+
Float(value.to_s) if value
|
6
|
+
rescue ArgumentError => exception
|
7
|
+
raise build_invalid_attribute_error(exception, value)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def build_invalid_attribute_error(exception, value)
|
13
|
+
payload = ExceptionPayload.new
|
14
|
+
payload.detail = I18n.t('fun_with_json_api.exceptions.invalid_float_attribute')
|
15
|
+
payload.pointer = "/data/attributes/#{name}"
|
16
|
+
Exceptions::InvalidAttribute.new(exception.message + ": #{value.inspect}", payload)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
class IntegerAttribute < FunWithJsonApi::Attribute
|
4
|
+
def call(value)
|
5
|
+
Integer(value.to_s) if value
|
6
|
+
rescue ArgumentError => exception
|
7
|
+
raise build_invalid_attribute_error(exception)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def build_invalid_attribute_error(exception)
|
13
|
+
payload = ExceptionPayload.new
|
14
|
+
payload.detail = I18n.t('fun_with_json_api.exceptions.invalid_integer_attribute')
|
15
|
+
payload.pointer = "/data/attributes/#{name}"
|
16
|
+
Exceptions::InvalidAttribute.new(exception.message, payload)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
class Relationship < FunWithJsonApi::Attribute
|
4
|
+
# Creates a new Relationship with name
|
5
|
+
# @param name [String] name of the relationship
|
6
|
+
# @param deserializer_class_or_callable [Class] Class of Deserializer or
|
7
|
+
# a callable that returns one
|
8
|
+
# @param options[at] [String] alias value for the attribute
|
9
|
+
def self.create(name, deserializer_class_or_callable, options = {})
|
10
|
+
new(name, deserializer_class_or_callable, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
delegate :id_param,
|
14
|
+
:type,
|
15
|
+
:resource_class,
|
16
|
+
to: :deserializer
|
17
|
+
|
18
|
+
def initialize(name, deserializer_class, options = {})
|
19
|
+
super(name, options)
|
20
|
+
@deserializer_class = deserializer_class
|
21
|
+
end
|
22
|
+
|
23
|
+
def deserializer
|
24
|
+
@deserializer ||= create_deserializer_from_deserializer_class
|
25
|
+
end
|
26
|
+
|
27
|
+
def call(id_value)
|
28
|
+
unless id_value.nil? || !id_value.is_a?(Array)
|
29
|
+
raise build_invalid_relationship_error(id_value)
|
30
|
+
end
|
31
|
+
|
32
|
+
resource_class.find_by!(id_param => id_value).try(:id) if id_value
|
33
|
+
rescue ActiveRecord::RecordNotFound => e
|
34
|
+
raise convert_record_not_found_error(e, id_value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def param_value
|
38
|
+
:"#{as}_id"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Creates a new Deserializer from the deserializer class
|
44
|
+
def create_deserializer_from_deserializer_class
|
45
|
+
if @deserializer_class.respond_to?(:call)
|
46
|
+
@deserializer_class.call
|
47
|
+
else
|
48
|
+
@deserializer_class
|
49
|
+
end.create(
|
50
|
+
attributes: [],
|
51
|
+
relationships: []
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_invalid_relationship_error(id_value)
|
56
|
+
exception_message = "#{name} relationship should contain a single '#{type}' data hash"
|
57
|
+
payload = ExceptionPayload.new
|
58
|
+
payload.pointer = "/data/relationships/#{name}"
|
59
|
+
payload.detail = exception_message
|
60
|
+
Exceptions::InvalidRelationship.new(exception_message + ": #{id_value.inspect}", payload)
|
61
|
+
end
|
62
|
+
|
63
|
+
def convert_record_not_found_error(exception, id_value)
|
64
|
+
payload = ExceptionPayload.new
|
65
|
+
payload.pointer = "/data/relationships/#{name}/id"
|
66
|
+
payload.detail = "Unable to find '#{type}' with matching id: #{id_value.inspect}"
|
67
|
+
exception_message = "Couldn't find #{resource_class} where "\
|
68
|
+
"#{id_param} = #{id_value.inspect}: #{exception.message}"
|
69
|
+
Exceptions::MissingRelationship.new(exception_message, payload)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Attributes
|
3
|
+
class RelationshipCollection < FunWithJsonApi::Attribute
|
4
|
+
def self.create(name, deserializer_class_or_callable, options = {})
|
5
|
+
new(name, deserializer_class_or_callable, options)
|
6
|
+
end
|
7
|
+
|
8
|
+
delegate :id_param,
|
9
|
+
:type,
|
10
|
+
:resource_class,
|
11
|
+
to: :deserializer
|
12
|
+
|
13
|
+
def initialize(name, deserializer_class, options = {})
|
14
|
+
super(name, options.reverse_merge(as: name.to_s.singularize.to_sym))
|
15
|
+
@deserializer_class = deserializer_class
|
16
|
+
|
17
|
+
if as.to_s != as.to_s.singularize
|
18
|
+
raise ArgumentError, "Use a singular relationship as value: {as: :#{as.to_s.singularize}}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def deserializer
|
23
|
+
@deserializer ||= create_deserializer_from_deserializer_class
|
24
|
+
end
|
25
|
+
|
26
|
+
# Expects an array of id values for a nested collection
|
27
|
+
def call(values)
|
28
|
+
unless values.nil? || values.is_a?(Array)
|
29
|
+
raise build_invalid_relationship_collection_error(values)
|
30
|
+
end
|
31
|
+
|
32
|
+
collection = resource_class.where(id_param => values)
|
33
|
+
|
34
|
+
# Ensure the collection size matches
|
35
|
+
expected_size = values.size
|
36
|
+
result_size = collection.size
|
37
|
+
if result_size != expected_size
|
38
|
+
raise build_missing_relationship_error_from_collection(collection, values)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Call ActiceRecord#pluck if it is available
|
42
|
+
convert_collection_to_ids(collection)
|
43
|
+
end
|
44
|
+
|
45
|
+
# User the singular of `as` that is how AMS converts the value
|
46
|
+
def param_value
|
47
|
+
:"#{as}_ids"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def convert_collection_to_ids(collection)
|
53
|
+
if collection.respond_to? :pluck
|
54
|
+
# Well... pluck+arel doesn't work with SQLite, but select at least is safe
|
55
|
+
collection = collection.select(resource_class.arel_table[:id])
|
56
|
+
end
|
57
|
+
collection.map(&:id)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Creates a new Deserializer from the deserializer class
|
61
|
+
def create_deserializer_from_deserializer_class
|
62
|
+
if @deserializer_class.respond_to?(:call)
|
63
|
+
@deserializer_class.call
|
64
|
+
else
|
65
|
+
@deserializer_class
|
66
|
+
end.create(
|
67
|
+
attributes: [],
|
68
|
+
relationships: []
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_invalid_relationship_collection_error(values)
|
73
|
+
exception_message = "#{name} relationship should contain a array of '#{type}' data"
|
74
|
+
payload = ExceptionPayload.new
|
75
|
+
payload.pointer = "/data/relationships/#{name}"
|
76
|
+
payload.detail = exception_message
|
77
|
+
Exceptions::InvalidRelationship.new(exception_message + ": #{values.inspect}", payload)
|
78
|
+
end
|
79
|
+
|
80
|
+
def build_missing_relationship_error_from_collection(collection, values)
|
81
|
+
collection_values = collection.map { |resource| resource.public_send(id_param).to_s }
|
82
|
+
missing_values = values.reject { |value| collection_values.include?(value.to_s) }
|
83
|
+
payload = missing_values.map do |value|
|
84
|
+
build_missing_relationship_payload(value)
|
85
|
+
end
|
86
|
+
exception_message = "Couldn't find #{resource_class} items with "\
|
87
|
+
"#{id_param} in #{missing_values.inspect}"
|
88
|
+
Exceptions::MissingRelationship.new(exception_message, payload)
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_missing_relationship_payload(value)
|
92
|
+
ExceptionPayload.new.tap do |payload|
|
93
|
+
payload.pointer = "/data/relationships/#{name}/id"
|
94
|
+
payload.detail = "Unable to find '#{type}' with matching id: #{value.inspect}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'fun_with_json_api/exception_serializer'
|
2
|
+
|
3
|
+
module FunWithJsonApi
|
4
|
+
module ControllerMethods
|
5
|
+
def render_fun_with_json_api_exception(exception)
|
6
|
+
render json: exception,
|
7
|
+
serializer: FunWithJsonApi::ExceptionSerializer,
|
8
|
+
adapter: :json,
|
9
|
+
status: exception.http_status
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|