erratum 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.txt +19 -0
- data/README.md +2 -0
- data/Rakefile +2 -0
- data/lib/erratum/configuration.rb +44 -0
- data/lib/erratum/error.rb +110 -0
- data/lib/erratum/errors/authentication_error.rb +9 -0
- data/lib/erratum/errors/authentication_errors/duplicate_authentication_error.rb +37 -0
- data/lib/erratum/errors/authentication_errors/invalid_token_error.rb +30 -0
- data/lib/erratum/errors/authentication_errors/invalid_username_or_password_error.rb +30 -0
- data/lib/erratum/errors/authorization_error.rb +9 -0
- data/lib/erratum/errors/authorization_errors/forbidden_error.rb +48 -0
- data/lib/erratum/errors/crud_error.rb +24 -0
- data/lib/erratum/errors/crud_errors/association_error.rb +54 -0
- data/lib/erratum/errors/crud_errors/resource_not_found_error.rb +52 -0
- data/lib/erratum/errors/crud_errors/resource_persistence_error.rb +50 -0
- data/lib/erratum/errors/request_errors/parameter_missing_error.rb +43 -0
- data/lib/erratum/errors/request_errors/unpermitted_parameters_error.rb +49 -0
- data/lib/erratum/rescuable_resource.rb +29 -0
- data/lib/erratum/resource_naming.rb +31 -0
- data/lib/erratum/utilities/string.rb +18 -0
- data/lib/erratum/verifiable_resource.rb +23 -0
- data/lib/erratum/version.rb +4 -0
- data/lib/erratum.rb +48 -0
- data/spec/lib/erratum/configuration_spec.rb +27 -0
- data/spec/lib/erratum/error_spec.rb +189 -0
- data/spec/lib/erratum/errors/authentication_errors/duplicate_authentication_error_spec.rb +43 -0
- data/spec/lib/erratum/errors/authentication_errors/invalid_token_error_spec.rb +33 -0
- data/spec/lib/erratum/errors/authentication_errors/invalid_username_or_password_error_spec.rb +35 -0
- data/spec/lib/erratum/errors/authorization_errors/forbidden_error_spec.rb +52 -0
- data/spec/lib/erratum/errors/crud_errors/association_error_spec.rb +69 -0
- data/spec/lib/erratum/errors/crud_errors/resource_not_found_error_spec.rb +89 -0
- data/spec/lib/erratum/errors/crud_errors/resource_persistence_error_spec.rb +84 -0
- data/spec/lib/erratum/errors/request_errors/parameter_missing_error_spec.rb +58 -0
- data/spec/lib/erratum/errors/request_errors/unpermitted_parameters_error_spec.rb +67 -0
- data/spec/lib/erratum/rescuable_resource_spec.rb +8 -0
- data/spec/lib/human_error_spec.rb +20 -0
- data.tar.gz.sig +0 -0
- metadata +171 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c87aeb2c02c44b93d2d11f4c3c253fe229b72c44
|
4
|
+
data.tar.gz: d596655b0085b9e0a22e1725235282a2c1f87964
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9afbb9199a4a7972ed0b0fea7ec56611ccb412bf504835eb58b1a102bcd3407a15add805c1bfbe1277dc9aa308256b2b1466eddbd9f00daf43aaef537e46966f
|
7
|
+
data.tar.gz: 7daab0272eeb312e81e663c792199d799e9511f596f9cde1f304c21659331b7d50b92acc38993d726f4c59507faffe414fc7192af72abbe3db4a1396c8c2aa52
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2016 The Grand Design
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
class Configuration
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_accessor :url_mappings
|
9
|
+
|
10
|
+
def external_documentation_urls
|
11
|
+
@external_documentation_urls ||= url_mappings['external_documentation_urls']
|
12
|
+
end
|
13
|
+
|
14
|
+
def developer_documentation_urls
|
15
|
+
@developer_documentation_urls ||= url_mappings['developer_documentation_urls']
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
{
|
20
|
+
external_documentation_urls: external_documentation_urls,
|
21
|
+
developer_documentation_urls: developer_documentation_urls,
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def url_mappings
|
26
|
+
@url_mappings ||= {
|
27
|
+
'external_documentation_urls' => {},
|
28
|
+
'developer_documentation_urls' => {},
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def configuration
|
34
|
+
Configuration.instance
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.configure
|
38
|
+
yield configuration
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.configuration
|
42
|
+
Configuration.instance
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'json'
|
3
|
+
require 'erratum/configuration'
|
4
|
+
require 'erratum/utilities/string'
|
5
|
+
|
6
|
+
class Erratum
|
7
|
+
module Error
|
8
|
+
module ClassMethods
|
9
|
+
def wrap(other)
|
10
|
+
wrapped_error = new message: "#{other.class.name}: #{other.message}"
|
11
|
+
wrapped_error.set_backtrace other.backtrace
|
12
|
+
wrapped_error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :id,
|
17
|
+
:external_documentation_url,
|
18
|
+
:developer_documentation_url,
|
19
|
+
:http_status,
|
20
|
+
:code,
|
21
|
+
:title,
|
22
|
+
:detail,
|
23
|
+
:source,
|
24
|
+
:message
|
25
|
+
|
26
|
+
def initialize(**args)
|
27
|
+
args.each do |variable, value|
|
28
|
+
public_send("#{variable}=", value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def as_json(_options = {})
|
33
|
+
{
|
34
|
+
errors: [
|
35
|
+
{
|
36
|
+
id: id,
|
37
|
+
links: {
|
38
|
+
about: external_documentation_url,
|
39
|
+
documentation: developer_documentation_url,
|
40
|
+
},
|
41
|
+
status: http_status,
|
42
|
+
code: code,
|
43
|
+
title: title,
|
44
|
+
detail: detail,
|
45
|
+
source: source,
|
46
|
+
},
|
47
|
+
],
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_json(_options = {})
|
52
|
+
JSON.dump(as_json)
|
53
|
+
end
|
54
|
+
|
55
|
+
def id
|
56
|
+
@id ||= SecureRandom.uuid
|
57
|
+
end
|
58
|
+
|
59
|
+
def external_documentation_url
|
60
|
+
@external_documentation_url ||= configuration.external_documentation_urls[code]
|
61
|
+
end
|
62
|
+
|
63
|
+
def developer_documentation_url
|
64
|
+
@developer_documentation_url ||= configuration.developer_documentation_urls[code]
|
65
|
+
end
|
66
|
+
|
67
|
+
def http_status
|
68
|
+
@http_status ||= 500
|
69
|
+
end
|
70
|
+
|
71
|
+
alias status http_status
|
72
|
+
|
73
|
+
def code
|
74
|
+
@code ||= Erratum::Utilities::String.
|
75
|
+
underscore(self.class.name).
|
76
|
+
gsub(%r{\A[^/]+/}, '').
|
77
|
+
gsub(%r{/}, '.')
|
78
|
+
end
|
79
|
+
|
80
|
+
def title
|
81
|
+
@title ||= self.class.name
|
82
|
+
end
|
83
|
+
|
84
|
+
def detail
|
85
|
+
@detail ||= 'The server encountered an error.'
|
86
|
+
end
|
87
|
+
|
88
|
+
def source
|
89
|
+
@source ||= {}
|
90
|
+
end
|
91
|
+
|
92
|
+
def message
|
93
|
+
to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
@message || detail
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.included(base)
|
101
|
+
base.extend ClassMethods
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def configuration
|
107
|
+
Erratum.configuration
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/errors/authentication_error'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module Errors
|
6
|
+
class DuplicateAuthenticationError < RuntimeError
|
7
|
+
include Error
|
8
|
+
include AuthenticationError
|
9
|
+
|
10
|
+
attr_accessor :provider,
|
11
|
+
:provider_user_id,
|
12
|
+
:user_id
|
13
|
+
|
14
|
+
def http_status
|
15
|
+
409
|
16
|
+
end
|
17
|
+
|
18
|
+
def title
|
19
|
+
'Duplicate Authentication'
|
20
|
+
end
|
21
|
+
|
22
|
+
def detail
|
23
|
+
'The authentication you attempted to register has already been registered by ' \
|
24
|
+
'another user. We do not currently support allowing multiple users to be connected ' \
|
25
|
+
'to the same authentication.'
|
26
|
+
end
|
27
|
+
|
28
|
+
def source
|
29
|
+
{
|
30
|
+
'provider' => provider,
|
31
|
+
'provider_user_id' => provider_user_id,
|
32
|
+
'user_id' => user_id,
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/errors/authentication_error'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module Errors
|
6
|
+
class InvalidTokenError < RuntimeError
|
7
|
+
include Error
|
8
|
+
include AuthenticationError
|
9
|
+
|
10
|
+
attr_accessor :authentication_token
|
11
|
+
|
12
|
+
def http_status
|
13
|
+
401
|
14
|
+
end
|
15
|
+
|
16
|
+
def title
|
17
|
+
'Invalid Token'
|
18
|
+
end
|
19
|
+
|
20
|
+
def detail
|
21
|
+
'The token you attempted to use for this request is invalid for this resource. ' \
|
22
|
+
'Please double-check and try again.'
|
23
|
+
end
|
24
|
+
|
25
|
+
def source
|
26
|
+
{ token: '[FILTERED]' }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/errors/authentication_error'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module Errors
|
6
|
+
class InvalidUsernameOrPasswordError < RuntimeError
|
7
|
+
include Error
|
8
|
+
include AuthenticationError
|
9
|
+
|
10
|
+
attr_accessor :username
|
11
|
+
|
12
|
+
def http_status
|
13
|
+
401
|
14
|
+
end
|
15
|
+
|
16
|
+
def title
|
17
|
+
'Invalid Username/Password'
|
18
|
+
end
|
19
|
+
|
20
|
+
def detail
|
21
|
+
'Either the username or password passed in or this request is invalid. Please ' \
|
22
|
+
'double-check and try again.'
|
23
|
+
end
|
24
|
+
|
25
|
+
def source
|
26
|
+
{ username: username, password: '[FILTERED]' }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/errors/authorization_error'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module Errors
|
6
|
+
class ForbiddenError < RuntimeError
|
7
|
+
include Error
|
8
|
+
include AuthorizationError
|
9
|
+
|
10
|
+
NON_SPECIFIC_RESOURCE_ACTIONS = %w{index create}.freeze
|
11
|
+
|
12
|
+
attr_accessor :resource_name,
|
13
|
+
:resource_id,
|
14
|
+
:action
|
15
|
+
|
16
|
+
def http_status
|
17
|
+
403
|
18
|
+
end
|
19
|
+
|
20
|
+
def title
|
21
|
+
'Forbidden'
|
22
|
+
end
|
23
|
+
|
24
|
+
def detail
|
25
|
+
detail_quantity = if NON_SPECIFIC_RESOURCE_ACTIONS.include? action.to_s
|
26
|
+
"#{action} a #{resource_name}"
|
27
|
+
else
|
28
|
+
"#{action} the #{resource_name} with ID #{resource_id}"
|
29
|
+
end
|
30
|
+
|
31
|
+
"You do not have access to #{detail_quantity}. Providing a different set of " \
|
32
|
+
"credentials may potentially allow you access to this resource."
|
33
|
+
end
|
34
|
+
|
35
|
+
def source
|
36
|
+
@source ||= {
|
37
|
+
'resource_name' => resource_name,
|
38
|
+
'resource_id' => resource_id,
|
39
|
+
'action' => @action,
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def action
|
44
|
+
@action || 'access'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Erratum
|
3
|
+
module Errors
|
4
|
+
module CrudError
|
5
|
+
attr_accessor :resource_name,
|
6
|
+
:action,
|
7
|
+
:resource_id
|
8
|
+
|
9
|
+
def initialize(resource_name: nil, action: nil, resource_id: nil, **args)
|
10
|
+
self.resource_name = resource_name
|
11
|
+
self.action = action || 'persist'
|
12
|
+
self.resource_id = resource_id
|
13
|
+
|
14
|
+
super(**args)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def resource_name_underscored
|
20
|
+
@resource_name_underscored ||= resource_name.gsub(/\s/, '_')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/errors/crud_error'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module Errors
|
6
|
+
class AssociationError < RuntimeError
|
7
|
+
include Error
|
8
|
+
include CrudError
|
9
|
+
|
10
|
+
attr_accessor :association_name,
|
11
|
+
:association_id,
|
12
|
+
:attributes
|
13
|
+
|
14
|
+
def self.convert(original_error, overrides = {})
|
15
|
+
initialization_parameters = {}
|
16
|
+
|
17
|
+
case original_error.class.name
|
18
|
+
when 'ActiveRecord::InvalidForeignKey'
|
19
|
+
message_info_pattern = /DETAIL: Key \((.*)_id\)=\(([a-f0-9\-]+)\)/
|
20
|
+
message_info = original_error.
|
21
|
+
message.
|
22
|
+
match(message_info_pattern)[1..-1]
|
23
|
+
|
24
|
+
initialization_parameters = {
|
25
|
+
association_name: message_info[0],
|
26
|
+
association_id: message_info[1],
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
new(initialization_parameters.merge(overrides))
|
31
|
+
end
|
32
|
+
|
33
|
+
def http_status
|
34
|
+
422
|
35
|
+
end
|
36
|
+
|
37
|
+
def title
|
38
|
+
'Association Error'
|
39
|
+
end
|
40
|
+
|
41
|
+
def detail
|
42
|
+
"The #{association_name} that you attempted to associate with " \
|
43
|
+
"the #{resource_name} was not valid."
|
44
|
+
end
|
45
|
+
|
46
|
+
def source
|
47
|
+
{
|
48
|
+
resource_name => attributes,
|
49
|
+
"#{association_name} id" => association_id,
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/errors/crud_error'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module Errors
|
6
|
+
class ResourceNotFoundError < RuntimeError
|
7
|
+
include Error
|
8
|
+
include CrudError
|
9
|
+
|
10
|
+
def self.convert(original_error, overrides = {})
|
11
|
+
initialization_parameters = {}
|
12
|
+
|
13
|
+
case original_error.class.name
|
14
|
+
when 'ActiveRecord::RecordNotFound'
|
15
|
+
initialization_parameters = {
|
16
|
+
resource_id: case original_error.message
|
17
|
+
when /\ACouldn't find .* without an ID\z/
|
18
|
+
[]
|
19
|
+
when /\ACouldn't find .* with \'.*\'=([a-f0-9\-]+)/
|
20
|
+
[Regexp.last_match(1)]
|
21
|
+
when /\ACouldn't find all .* with \'.*\': ((?:[a-f0-9\-]+(?:, )?)+)/
|
22
|
+
Array(Regexp.last_match(1).split(', '))
|
23
|
+
end,
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
new(initialization_parameters.merge(overrides))
|
28
|
+
end
|
29
|
+
|
30
|
+
def http_status
|
31
|
+
404
|
32
|
+
end
|
33
|
+
|
34
|
+
def title
|
35
|
+
'Resource Not Found'
|
36
|
+
end
|
37
|
+
|
38
|
+
def detail
|
39
|
+
"The #{resource_name} you attempted to #{action} for this request is either " \
|
40
|
+
"not authorized for the authenticated user or does not exist."
|
41
|
+
end
|
42
|
+
|
43
|
+
def source
|
44
|
+
{ "#{resource_name_underscored}_id" => resource_id }
|
45
|
+
end
|
46
|
+
|
47
|
+
def action
|
48
|
+
@action || 'access'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/errors/crud_error'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module Errors
|
6
|
+
class ResourcePersistenceError < RuntimeError
|
7
|
+
include Error
|
8
|
+
include CrudError
|
9
|
+
|
10
|
+
attr_accessor :errors,
|
11
|
+
:attributes
|
12
|
+
|
13
|
+
def self.convert(original_error, overrides = {})
|
14
|
+
initialization_parameters = {}
|
15
|
+
|
16
|
+
case original_error.class.name
|
17
|
+
when 'ActiveRecord::RecordInvalid',
|
18
|
+
'ActiveRecord::RecordNotSaved'
|
19
|
+
|
20
|
+
initialization_parameters = {
|
21
|
+
attributes: original_error.record.attributes,
|
22
|
+
errors: original_error.record.errors.full_messages,
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
new(initialization_parameters.merge(overrides))
|
27
|
+
end
|
28
|
+
|
29
|
+
def http_status
|
30
|
+
422
|
31
|
+
end
|
32
|
+
|
33
|
+
def title
|
34
|
+
'Resource Persistence Error'
|
35
|
+
end
|
36
|
+
|
37
|
+
def detail
|
38
|
+
"One or more of the attributes on the #{resource_name} you attempted " \
|
39
|
+
"to #{action} is invalid."
|
40
|
+
end
|
41
|
+
|
42
|
+
def source
|
43
|
+
{
|
44
|
+
'errors' => errors,
|
45
|
+
'attributes' => attributes,
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Erratum
|
3
|
+
module Errors
|
4
|
+
class ParameterMissingError < RuntimeError
|
5
|
+
include Error
|
6
|
+
include CrudError
|
7
|
+
|
8
|
+
attr_accessor :parameter
|
9
|
+
|
10
|
+
def self.convert(original_error, overrides = {})
|
11
|
+
initialization_parameters = {}
|
12
|
+
|
13
|
+
case original_error.class.name
|
14
|
+
when 'ActionController::ParameterMissing'
|
15
|
+
initialization_parameters = {
|
16
|
+
parameter: original_error.param,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
new(initialization_parameters.merge(overrides))
|
21
|
+
end
|
22
|
+
|
23
|
+
def http_status
|
24
|
+
400
|
25
|
+
end
|
26
|
+
|
27
|
+
def title
|
28
|
+
'Missing Parameter'
|
29
|
+
end
|
30
|
+
|
31
|
+
def detail
|
32
|
+
"When attempting to #{action} a #{resource_name}, '#{parameter}' is a " \
|
33
|
+
"required parameter."
|
34
|
+
end
|
35
|
+
|
36
|
+
def source
|
37
|
+
{
|
38
|
+
'required_parameter' => parameter,
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Erratum
|
3
|
+
module Errors
|
4
|
+
class UnpermittedParametersError < RuntimeError
|
5
|
+
include Error
|
6
|
+
include CrudError
|
7
|
+
|
8
|
+
attr_accessor :parameters
|
9
|
+
|
10
|
+
def self.convert(original_error, overrides = {})
|
11
|
+
initialization_parameters = {}
|
12
|
+
|
13
|
+
case original_error.class.name
|
14
|
+
when 'ActionController::UnpermittedParameters'
|
15
|
+
initialization_parameters = {
|
16
|
+
parameters: Array(original_error.params),
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
new(initialization_parameters.merge(overrides))
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(**attrs)
|
24
|
+
self.parameters = Array(attrs.delete(:parameters))
|
25
|
+
|
26
|
+
super(**attrs)
|
27
|
+
end
|
28
|
+
|
29
|
+
def http_status
|
30
|
+
400
|
31
|
+
end
|
32
|
+
|
33
|
+
def title
|
34
|
+
'Unpermitted Parameters'
|
35
|
+
end
|
36
|
+
|
37
|
+
def detail
|
38
|
+
"Attempting to #{action} a #{resource_name} with the following parameters is " \
|
39
|
+
"not allowed: #{parameters.join(', ')}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def source
|
43
|
+
{
|
44
|
+
'unpermitted_parameters' => parameters,
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum/resource_naming'
|
3
|
+
|
4
|
+
class Erratum
|
5
|
+
module RescuableResource
|
6
|
+
def self.included(base)
|
7
|
+
base.include ResourceNaming
|
8
|
+
|
9
|
+
base.rescue_from 'ActiveRecord::RecordInvalid',
|
10
|
+
'ActiveRecord::RecordNotSaved',
|
11
|
+
'ActiveRecord::RecordNotFound',
|
12
|
+
'ActiveRecord::InvalidForeignKey',
|
13
|
+
'ActionController::ParameterMissing',
|
14
|
+
'ActionController::UnpermittedParameters' do |exception|
|
15
|
+
erratum = Erratum.convert(exception,
|
16
|
+
resource_name: self.class.singular_resource_name,
|
17
|
+
action: action_name)
|
18
|
+
|
19
|
+
render json: erratum,
|
20
|
+
status: erratum.http_status
|
21
|
+
end
|
22
|
+
|
23
|
+
base.rescue_from 'Erratum::Error' do |exception|
|
24
|
+
render json: exception,
|
25
|
+
status: exception.http_status
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|