human_error 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/human_error.rb +21 -19
- data/lib/human_error/configuration.rb +27 -7
- data/lib/human_error/error.rb +60 -28
- data/lib/human_error/errors/authentication_errors/duplicate_authentication_error.rb +8 -8
- data/lib/human_error/errors/authentication_errors/invalid_token_error.rb +8 -8
- data/lib/human_error/errors/authentication_errors/invalid_username_or_password_error.rb +8 -8
- data/lib/human_error/errors/crud_errors/association_error.rb +8 -9
- data/lib/human_error/errors/crud_errors/resource_not_found_error.rb +8 -8
- data/lib/human_error/errors/crud_errors/resource_persistence_error.rb +8 -8
- data/lib/human_error/errors/request_errors/parameter_missing_error.rb +40 -0
- data/lib/human_error/errors/request_errors/unpermitted_parameters_error.rb +46 -0
- data/lib/human_error/rescuable_resource.rb +30 -47
- data/lib/human_error/verifiable_resource.rb +36 -0
- data/lib/human_error/version.rb +1 -1
- data/spec/lib/human_error/configuration_spec.rb +13 -26
- data/spec/lib/human_error/error_spec.rb +135 -1
- data/spec/lib/human_error/errors/authentication_errors/duplicate_authentication_error_spec.rb +8 -13
- data/spec/lib/human_error/errors/authentication_errors/invalid_token_error_spec.rb +8 -12
- data/spec/lib/human_error/errors/authentication_errors/invalid_username_or_password_error_spec.rb +8 -13
- data/spec/lib/human_error/errors/crud_errors/association_error_spec.rb +8 -16
- data/spec/lib/human_error/errors/crud_errors/resource_not_found_error_spec.rb +9 -17
- data/spec/lib/human_error/errors/crud_errors/resource_persistence_error_spec.rb +9 -17
- data/spec/lib/human_error/errors/request_errors/parameter_missing_error_spec.rb +55 -0
- data/spec/lib/human_error/errors/request_errors/unpermitted_parameters_error_spec.rb +62 -0
- data/spec/lib/human_error_spec.rb +4 -57
- metadata +26 -12
- data/lib/human_error/error_code_directory.rb +0 -20
- data/lib/human_error/errors.rb +0 -9
- data/lib/human_error/errors/request_error.rb +0 -48
- data/lib/human_error/knowledgebase_id_directory.rb +0 -20
- data/lib/human_error/verifiable_model.rb +0 -30
- data/spec/lib/human_error/errors/request_error_spec.rb +0 -95
@@ -0,0 +1,40 @@
|
|
1
|
+
class HumanError
|
2
|
+
module Errors
|
3
|
+
class ParameterMissingError < RuntimeError
|
4
|
+
include Error
|
5
|
+
|
6
|
+
attr_accessor :parameter
|
7
|
+
|
8
|
+
def self.convert(original_error, overrides = {})
|
9
|
+
initialization_parameters = {}
|
10
|
+
|
11
|
+
case original_error.class.name
|
12
|
+
when 'ActionController::ParameterMissing'
|
13
|
+
initialization_parameters = {
|
14
|
+
parameter: original_error.param,
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
new(initialization_parameters.merge(overrides))
|
19
|
+
end
|
20
|
+
|
21
|
+
def http_status
|
22
|
+
400
|
23
|
+
end
|
24
|
+
|
25
|
+
def title
|
26
|
+
'Missing Parameter'
|
27
|
+
end
|
28
|
+
|
29
|
+
def detail
|
30
|
+
"#{parameter} is a required parameter, but you did not supply it."
|
31
|
+
end
|
32
|
+
|
33
|
+
def source
|
34
|
+
{
|
35
|
+
'required_parameter' => parameter,
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class HumanError
|
2
|
+
module Errors
|
3
|
+
class UnpermittedParametersError < RuntimeError
|
4
|
+
include Error
|
5
|
+
|
6
|
+
attr_accessor :parameters
|
7
|
+
|
8
|
+
def self.convert(original_error, overrides = {})
|
9
|
+
initialization_parameters = {}
|
10
|
+
|
11
|
+
case original_error.class.name
|
12
|
+
when 'ActionController::UnpermittedParameters'
|
13
|
+
initialization_parameters = {
|
14
|
+
parameters: Array(original_error.params),
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
new(initialization_parameters.merge(overrides))
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(**attrs)
|
22
|
+
self.parameters = Array(attrs.delete(:parameters))
|
23
|
+
|
24
|
+
super(**attrs)
|
25
|
+
end
|
26
|
+
|
27
|
+
def http_status
|
28
|
+
400
|
29
|
+
end
|
30
|
+
|
31
|
+
def title
|
32
|
+
'Unpermitted Parameters'
|
33
|
+
end
|
34
|
+
|
35
|
+
def detail
|
36
|
+
"The following parameters passed are not allowed: #{parameters.join(', ')}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def source
|
40
|
+
{
|
41
|
+
'unpermitted_parameters' => parameters,
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,59 +1,42 @@
|
|
1
|
-
require 'human_error/errors/crud_errors/resource_persistence_error'
|
2
|
-
require 'human_error/errors/crud_errors/resource_not_found_error'
|
3
|
-
require 'human_error/errors/crud_errors/association_error'
|
4
|
-
|
5
1
|
class HumanError
|
6
2
|
module RescuableResource
|
7
3
|
module ClassMethods
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
rescue_from 'ActiveRecord::RecordInvalid',
|
15
|
-
'ActiveRecord::RecordNotSaved' do |exception|
|
16
|
-
human_error = lookup_library.convert(exception,
|
17
|
-
resource_name: nice_resource_name,
|
18
|
-
action: action_name)
|
19
|
-
|
20
|
-
render json: human_error,
|
21
|
-
status: human_error.http_status
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
if from.include? 'not_found'
|
26
|
-
rescue_from 'ActiveRecord::RecordNotFound' do |exception|
|
27
|
-
human_error = lookup_library.convert(exception,
|
28
|
-
resource_name: nice_resource_name,
|
29
|
-
action: action_name)
|
30
|
-
|
31
|
-
render json: human_error,
|
32
|
-
status: human_error.http_status
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
if from.include? 'association'
|
37
|
-
rescue_from 'ActiveRecord::InvalidForeignKey' do |exception|
|
38
|
-
human_error = lookup_library.convert(exception,
|
39
|
-
resource_name: nice_resource_name,
|
40
|
-
action: action_name)
|
41
|
-
|
42
|
-
render json: human_error,
|
43
|
-
status: human_error.http_status
|
44
|
-
end
|
45
|
-
end
|
4
|
+
def plural_resource_name
|
5
|
+
name[/(\w+)Controller\z/, 1].
|
6
|
+
underscore.
|
7
|
+
pluralize.
|
8
|
+
downcase
|
9
|
+
end
|
46
10
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
11
|
+
def singular_resource_name
|
12
|
+
name[/(\w+)Controller\z/, 1].
|
13
|
+
underscore.
|
14
|
+
singularize.
|
15
|
+
downcase
|
51
16
|
end
|
52
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Style/GuardClause
|
53
17
|
end
|
54
18
|
|
55
19
|
def self.included(base)
|
56
20
|
base.extend ClassMethods
|
21
|
+
|
22
|
+
base.rescue_from 'ActiveRecord::RecordInvalid',
|
23
|
+
'ActiveRecord::RecordNotSaved',
|
24
|
+
'ActiveRecord::RecordNotFound',
|
25
|
+
'ActiveRecord::InvalidForeignKey',
|
26
|
+
'ActionController::ParameterMissing',
|
27
|
+
'ActionController::UnpermittedParameters' do |exception|
|
28
|
+
human_error = HumanError.convert(exception,
|
29
|
+
resource_name: self.class.singular_resource_name,
|
30
|
+
action: action_name)
|
31
|
+
|
32
|
+
render json: human_error,
|
33
|
+
status: human_error.http_status
|
34
|
+
end
|
35
|
+
|
36
|
+
base.rescue_from 'HumanError::Error' do |exception|
|
37
|
+
render json: exception,
|
38
|
+
status: exception.http_status
|
39
|
+
end
|
57
40
|
end
|
58
41
|
end
|
59
42
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class HumanError
|
2
|
+
module VerifiableResource
|
3
|
+
module ClassMethods
|
4
|
+
def plural_resource_name
|
5
|
+
name[/(\w+)Controller\z/, 1].
|
6
|
+
underscore.
|
7
|
+
pluralize.
|
8
|
+
downcase
|
9
|
+
end
|
10
|
+
|
11
|
+
def singular_resource_name
|
12
|
+
name[/(\w+)Controller\z/, 1].
|
13
|
+
underscore.
|
14
|
+
singularize.
|
15
|
+
downcase
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(base)
|
20
|
+
base.extend ClassMethods
|
21
|
+
|
22
|
+
base.before_action except: %i{index create} do
|
23
|
+
model = public_send(self.class.singular_resource_name)
|
24
|
+
|
25
|
+
resource_not_found_error = HumanError.build(
|
26
|
+
'ResourceNotFoundError',
|
27
|
+
resource_name: self.class.singular_resource_name,
|
28
|
+
resource_id: [params[:id]],
|
29
|
+
action: action_name,
|
30
|
+
)
|
31
|
+
|
32
|
+
fail resource_not_found_error unless model.persisted?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/human_error/version.rb
CHANGED
@@ -2,38 +2,25 @@ require 'rspectacular'
|
|
2
2
|
require 'human_error/configuration'
|
3
3
|
|
4
4
|
class HumanError
|
5
|
-
describe Configuration do
|
6
|
-
it 'can set the
|
7
|
-
configuration = Configuration.
|
8
|
-
configuration.
|
5
|
+
describe Configuration, singletons: true do
|
6
|
+
it 'can set the url mappings' do
|
7
|
+
configuration = Configuration.instance
|
8
|
+
configuration.url_mappings = 'whateeeeeever'
|
9
9
|
|
10
|
-
expect(configuration.
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'can set the API error documentation URL' do
|
14
|
-
configuration = Configuration.new
|
15
|
-
configuration.api_error_documentation_url = 'whateeeeeever'
|
16
|
-
|
17
|
-
expect(configuration.api_error_documentation_url).to eql 'whateeeeeever'
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'can set the knowledgebase URL' do
|
21
|
-
configuration = Configuration.new
|
22
|
-
configuration.knowledgebase_url = 'whateeeeeever'
|
23
|
-
|
24
|
-
expect(configuration.knowledgebase_url).to eql 'whateeeeeever'
|
10
|
+
expect(configuration.url_mappings).to eql 'whateeeeeever'
|
25
11
|
end
|
26
12
|
|
27
13
|
it 'can convert itself into a hash' do
|
28
|
-
configuration
|
29
|
-
configuration.
|
30
|
-
|
31
|
-
|
14
|
+
configuration = Configuration.instance
|
15
|
+
configuration.url_mappings = {
|
16
|
+
'external_documentation_urls' => 'blah',
|
17
|
+
'developer_documentation_urls' => 'asdf',
|
18
|
+
}
|
32
19
|
|
33
20
|
expect(configuration.to_h).to eql(
|
34
|
-
|
35
|
-
|
36
|
-
|
21
|
+
external_documentation_urls: 'blah',
|
22
|
+
developer_documentation_urls: 'asdf',
|
23
|
+
)
|
37
24
|
end
|
38
25
|
end
|
39
26
|
end
|
@@ -37,10 +37,144 @@ describe Error do
|
|
37
37
|
|
38
38
|
it 'can have its message explicitly set when it is generated' do
|
39
39
|
error = CustomError.new
|
40
|
-
allow(error).to receive(:
|
40
|
+
allow(error).to receive(:detail).
|
41
41
|
and_return('My Developer Message')
|
42
42
|
|
43
43
|
expect(error.message).to eql 'My Developer Message'
|
44
44
|
end
|
45
|
+
|
46
|
+
it 'can generate error data' do
|
47
|
+
custom_error = CustomError.new(
|
48
|
+
id: 'identifier',
|
49
|
+
http_status: 'flibbity',
|
50
|
+
code: 'jibbit',
|
51
|
+
title: 'roll dem bones and stones',
|
52
|
+
detail: 'I cannot receive any satisfaction',
|
53
|
+
source: 'But perhaps if I attempt it one more time, I can',
|
54
|
+
developer_documentation_url: 'asimof/jibbit?version=janky',
|
55
|
+
external_documentation_url: 'jinkies/87654321')
|
56
|
+
|
57
|
+
expect(custom_error.as_json).to eql(
|
58
|
+
errors: [
|
59
|
+
{
|
60
|
+
id: 'identifier',
|
61
|
+
links: {
|
62
|
+
about: 'jinkies/87654321',
|
63
|
+
documentation: 'asimof/jibbit?version=janky',
|
64
|
+
},
|
65
|
+
status: 'flibbity',
|
66
|
+
code: 'jibbit',
|
67
|
+
title: 'roll dem bones and stones',
|
68
|
+
detail: 'I cannot receive any satisfaction',
|
69
|
+
source: 'But perhaps if I attempt it one more time, I can',
|
70
|
+
},
|
71
|
+
])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can extract configuration from the global config if it is not passed in',
|
75
|
+
singletons: HumanError::Configuration do
|
76
|
+
|
77
|
+
HumanError.configure do |config|
|
78
|
+
config.url_mappings = {
|
79
|
+
'external_documentation_urls' => {
|
80
|
+
'jibbit' => 'http://example.com/edu',
|
81
|
+
},
|
82
|
+
'developer_documentation_urls' => {
|
83
|
+
'jibbit' => 'http://example.com/ddu',
|
84
|
+
},
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
custom_error = CustomError.new(
|
89
|
+
id: 'identifier',
|
90
|
+
http_status: 'flibbity',
|
91
|
+
code: 'jibbit',
|
92
|
+
title: 'roll dem bones and stones',
|
93
|
+
detail: 'I cannot receive any satisfaction',
|
94
|
+
source: 'But perhaps if I attempt it one more time, I can')
|
95
|
+
|
96
|
+
expect(custom_error.as_json).to eql(
|
97
|
+
errors: [
|
98
|
+
{
|
99
|
+
id: 'identifier',
|
100
|
+
links: {
|
101
|
+
about: 'http://example.com/edu',
|
102
|
+
documentation: 'http://example.com/ddu',
|
103
|
+
},
|
104
|
+
status: 'flibbity',
|
105
|
+
code: 'jibbit',
|
106
|
+
title: 'roll dem bones and stones',
|
107
|
+
detail: 'I cannot receive any satisfaction',
|
108
|
+
source: 'But perhaps if I attempt it one more time, I can',
|
109
|
+
},
|
110
|
+
])
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'can override the global config if it is set, but an explicit value is passed in',
|
114
|
+
singletons: HumanError::Configuration do
|
115
|
+
|
116
|
+
HumanError.configure do |config|
|
117
|
+
config.url_mappings = {
|
118
|
+
'external_documentation_urls' => {
|
119
|
+
'jibbit' => 'http://example.com/edu',
|
120
|
+
},
|
121
|
+
'developer_documentation_urls' => {
|
122
|
+
'jibbit' => 'http://example.com/ddu',
|
123
|
+
},
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
custom_error = CustomError.new(
|
128
|
+
id: 'identifier',
|
129
|
+
http_status: 'flibbity',
|
130
|
+
code: 'jibbit',
|
131
|
+
title: 'roll dem bones and stones',
|
132
|
+
detail: 'I cannot receive any satisfaction',
|
133
|
+
source: 'But perhaps if I attempt it one more time, I can',
|
134
|
+
developer_documentation_url: 'hasimof/jibbit?version=hanky',
|
135
|
+
external_documentation_url: 'hinkies/87654321')
|
136
|
+
|
137
|
+
expect(custom_error.as_json).to eql(
|
138
|
+
errors: [
|
139
|
+
{
|
140
|
+
id: 'identifier',
|
141
|
+
links: {
|
142
|
+
about: 'hinkies/87654321',
|
143
|
+
documentation: 'hasimof/jibbit?version=hanky',
|
144
|
+
},
|
145
|
+
status: 'flibbity',
|
146
|
+
code: 'jibbit',
|
147
|
+
title: 'roll dem bones and stones',
|
148
|
+
detail: 'I cannot receive any satisfaction',
|
149
|
+
source: 'But perhaps if I attempt it one more time, I can',
|
150
|
+
},
|
151
|
+
])
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'can handle if it finds no URL mappings' do
|
155
|
+
custom_error = CustomError.new(
|
156
|
+
id: 'identifier',
|
157
|
+
http_status: 'flibbity',
|
158
|
+
code: 'jibbit',
|
159
|
+
title: 'roll dem bones and stones',
|
160
|
+
detail: 'I cannot receive any satisfaction',
|
161
|
+
source: 'But perhaps if I attempt it one more time, I can')
|
162
|
+
|
163
|
+
expect(custom_error.as_json).to eql(
|
164
|
+
errors: [
|
165
|
+
{
|
166
|
+
id: 'identifier',
|
167
|
+
links: {
|
168
|
+
about: nil,
|
169
|
+
documentation: nil,
|
170
|
+
},
|
171
|
+
status: 'flibbity',
|
172
|
+
code: 'jibbit',
|
173
|
+
title: 'roll dem bones and stones',
|
174
|
+
detail: 'I cannot receive any satisfaction',
|
175
|
+
source: 'But perhaps if I attempt it one more time, I can',
|
176
|
+
},
|
177
|
+
])
|
178
|
+
end
|
45
179
|
end
|
46
180
|
end
|
data/spec/lib/human_error/errors/authentication_errors/duplicate_authentication_error_spec.rb
CHANGED
@@ -14,34 +14,29 @@ describe DuplicateAuthenticationError do
|
|
14
14
|
expect(error.http_status).to eql 409
|
15
15
|
end
|
16
16
|
|
17
|
-
it 'has a code
|
18
|
-
expect(error.code).to eql
|
17
|
+
it 'has a code' do
|
18
|
+
expect(error.code).to eql 'errors.duplicate_authentication_error'
|
19
19
|
end
|
20
20
|
|
21
|
-
it 'has a
|
22
|
-
expect(error.
|
21
|
+
it 'has a title' do
|
22
|
+
expect(error.title).to eql 'Duplicate Authentication'
|
23
23
|
end
|
24
24
|
|
25
|
-
it 'can output the
|
26
|
-
expect(error.
|
25
|
+
it 'can output the detail' do
|
26
|
+
expect(error.detail).to eql 'The authentication you attempted to ' \
|
27
27
|
'register has already been registered by ' \
|
28
28
|
'another user. We do not currently support ' \
|
29
29
|
'allowing multiple users to be connected to ' \
|
30
30
|
'the same authentication.'
|
31
31
|
end
|
32
32
|
|
33
|
-
it 'can output the
|
34
|
-
expect(error.
|
33
|
+
it 'can output the source' do
|
34
|
+
expect(error.source).to eql(
|
35
35
|
'provider' => 'flibbity',
|
36
36
|
'provider_user_id' => '12345',
|
37
37
|
'user_id' => '54321',
|
38
38
|
)
|
39
39
|
end
|
40
|
-
|
41
|
-
it 'can output the friendly message' do
|
42
|
-
expect(error.friendly_message).to eql 'Sorry! Someone else has already registered ' \
|
43
|
-
'this flibbity login.'
|
44
|
-
end
|
45
40
|
end
|
46
41
|
end
|
47
42
|
end
|