human_error 2.0.0 → 3.0.0
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/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
|