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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/human_error.rb +21 -19
  3. data/lib/human_error/configuration.rb +27 -7
  4. data/lib/human_error/error.rb +60 -28
  5. data/lib/human_error/errors/authentication_errors/duplicate_authentication_error.rb +8 -8
  6. data/lib/human_error/errors/authentication_errors/invalid_token_error.rb +8 -8
  7. data/lib/human_error/errors/authentication_errors/invalid_username_or_password_error.rb +8 -8
  8. data/lib/human_error/errors/crud_errors/association_error.rb +8 -9
  9. data/lib/human_error/errors/crud_errors/resource_not_found_error.rb +8 -8
  10. data/lib/human_error/errors/crud_errors/resource_persistence_error.rb +8 -8
  11. data/lib/human_error/errors/request_errors/parameter_missing_error.rb +40 -0
  12. data/lib/human_error/errors/request_errors/unpermitted_parameters_error.rb +46 -0
  13. data/lib/human_error/rescuable_resource.rb +30 -47
  14. data/lib/human_error/verifiable_resource.rb +36 -0
  15. data/lib/human_error/version.rb +1 -1
  16. data/spec/lib/human_error/configuration_spec.rb +13 -26
  17. data/spec/lib/human_error/error_spec.rb +135 -1
  18. data/spec/lib/human_error/errors/authentication_errors/duplicate_authentication_error_spec.rb +8 -13
  19. data/spec/lib/human_error/errors/authentication_errors/invalid_token_error_spec.rb +8 -12
  20. data/spec/lib/human_error/errors/authentication_errors/invalid_username_or_password_error_spec.rb +8 -13
  21. data/spec/lib/human_error/errors/crud_errors/association_error_spec.rb +8 -16
  22. data/spec/lib/human_error/errors/crud_errors/resource_not_found_error_spec.rb +9 -17
  23. data/spec/lib/human_error/errors/crud_errors/resource_persistence_error_spec.rb +9 -17
  24. data/spec/lib/human_error/errors/request_errors/parameter_missing_error_spec.rb +55 -0
  25. data/spec/lib/human_error/errors/request_errors/unpermitted_parameters_error_spec.rb +62 -0
  26. data/spec/lib/human_error_spec.rb +4 -57
  27. metadata +26 -12
  28. data/lib/human_error/error_code_directory.rb +0 -20
  29. data/lib/human_error/errors.rb +0 -9
  30. data/lib/human_error/errors/request_error.rb +0 -48
  31. data/lib/human_error/knowledgebase_id_directory.rb +0 -20
  32. data/lib/human_error/verifiable_model.rb +0 -30
  33. data/spec/lib/human_error/errors/request_error_spec.rb +0 -95
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 17aba5f28df14fa69f37785ddb08659985741d9b
4
- data.tar.gz: a39be0f21794151eaebf056663e327b3499b3b91
3
+ metadata.gz: ce3584e03d830added3a0b58d9fa67995aba2b62
4
+ data.tar.gz: ce828a6fd224bc186e2da06ce1c007ea3cbad3fa
5
5
  SHA512:
6
- metadata.gz: 88747c7a2352b0cdd2d7b9867c481f033b987f353b1af78faa649ebfeb6c015dc23fdf2b0b550eb01e63e0710d397549da10857ab72d85395ea28a2ca97bd8ec
7
- data.tar.gz: 1d4b15fbb41d93ed4eb13854d21d39535a7a402ecbbcb66b4fd4c9504ebcd656534efd3ba2081214539d0832bcf0cd5ed3eccfc1350d135a8c29e1ca70b75ce7
6
+ metadata.gz: 2383d7ae1f37c0ae716b39526cd6ade1365f09ffd9eaeb5784957e26f8cfa46e77c1593f64a0afc71c4e93627096d363887b31550125cfdd73be29d8b803ccb5
7
+ data.tar.gz: cb3efadd3dd1f368b5889474b27ca8830bd971470b18c4888589f09bcbddae1877a133f1d903268d6f14647273cf038aeaa1daeb970adc1848d3cfc1c4183209
@@ -1,31 +1,29 @@
1
- require 'human_error/version'
1
+ require 'human_error/configuration'
2
2
  require 'human_error/error'
3
- require 'human_error/errors'
3
+ require 'human_error/errors/authentication_error'
4
+ require 'human_error/errors/authentication_errors/duplicate_authentication_error'
5
+ require 'human_error/errors/authentication_errors/invalid_token_error'
6
+ require 'human_error/errors/authentication_errors/invalid_username_or_password_error'
7
+ require 'human_error/errors/crud_error'
8
+ require 'human_error/errors/crud_errors/association_error'
9
+ require 'human_error/errors/crud_errors/resource_not_found_error'
10
+ require 'human_error/errors/crud_errors/resource_persistence_error'
11
+ require 'human_error/errors/request_errors/parameter_missing_error'
12
+ require 'human_error/errors/request_errors/unpermitted_parameters_error'
4
13
  require 'human_error/rescuable_resource'
5
- require 'human_error/verifiable_model'
14
+ require 'human_error/verifiable_resource'
15
+ require 'human_error/version'
6
16
 
7
17
  class HumanError
8
- attr_accessor :configuration
9
-
10
- def initialize
11
- self.configuration = Configuration.new
12
-
13
- yield configuration if block_given?
14
- end
15
-
16
- def fetch(error_type)
18
+ def self.fetch(error_type)
17
19
  Object.const_get("HumanError::Errors::#{error_type}")
18
20
  end
19
21
 
20
- def build(error_type, overrides = {})
21
- overrides = configuration.to_h.merge(overrides)
22
-
22
+ def self.build(error_type, overrides = {})
23
23
  fetch(error_type).new(overrides)
24
24
  end
25
25
 
26
- def convert(original_error, overrides = {})
27
- overrides = configuration.to_h.merge(overrides)
28
-
26
+ def self.convert(original_error, overrides = {})
29
27
  case original_error.class.name
30
28
  when 'ActiveRecord::InvalidForeignKey'
31
29
  fetch('AssociationError').convert(original_error, overrides)
@@ -34,10 +32,14 @@ class HumanError
34
32
  when 'ActiveRecord::RecordInvalid',
35
33
  'ActiveRecord::RecordNotSaved'
36
34
  fetch('ResourcePersistenceError').convert(original_error, overrides)
35
+ when 'ActionController::ParameterMissing',
36
+ fetch('ParameterMissingError').convert(original_error, overrides)
37
+ when 'ActionController::UnpermittedParameters'
38
+ fetch('UnpermittedParametersError').convert(original_error, overrides)
37
39
  end
38
40
  end
39
41
 
40
- def raise(error_type, **args)
42
+ def self.raise(error_type, **args)
41
43
  Kernel.raise build(error_type, **args)
42
44
  end
43
45
  end
@@ -1,16 +1,36 @@
1
+ require 'singleton'
2
+
1
3
  class HumanError
2
4
  class Configuration
3
- attr_accessor :api_version,
4
- :api_error_documentation_url,
5
- :knowledgebase_url
5
+ include Singleton
6
+
7
+ attr_accessor :url_mappings
8
+
9
+ def external_documentation_urls
10
+ @external_documentation_urls ||= url_mappings['external_documentation_urls']
11
+ end
12
+
13
+ def developer_documentation_urls
14
+ @developer_documentation_urls ||= url_mappings['developer_documentation_urls']
15
+ end
6
16
 
7
17
  def to_h
8
18
  {
9
- knowledgebase_url: knowledgebase_url,
10
- api_error_documentation_url: api_error_documentation_url,
11
- api_version: api_version,
19
+ external_documentation_urls: external_documentation_urls,
20
+ developer_documentation_urls: developer_documentation_urls,
12
21
  }
13
22
  end
23
+
24
+ def url_mappings
25
+ @url_mappings ||= {
26
+ 'external_documentation_urls' => {},
27
+ 'developer_documentation_urls' => {},
28
+ }
29
+ end
30
+ end
31
+
32
+ def configuration
33
+ Configuration.instance
14
34
  end
15
35
 
16
36
  def self.configure
@@ -18,6 +38,6 @@ class HumanError
18
38
  end
19
39
 
20
40
  def self.configuration
21
- @configuration ||= Configuration.new
41
+ Configuration.instance
22
42
  end
23
43
  end
@@ -1,7 +1,6 @@
1
1
  require 'json'
2
2
  require 'human_error/configuration'
3
- require 'human_error/error_code_directory'
4
- require 'human_error/knowledgebase_id_directory'
3
+ require 'human_error/utilities/string'
5
4
 
6
5
  class HumanError
7
6
  module Error
@@ -13,41 +12,80 @@ module Error
13
12
  end
14
13
  end
15
14
 
16
- attr_accessor :api_version,
17
- :api_error_documentation_url,
18
- :knowledgebase_url,
19
- :knowledgebase_article_id,
15
+ attr_accessor :id,
16
+ :external_documentation_url,
17
+ :developer_documentation_url,
18
+ :http_status,
20
19
  :code,
20
+ :title,
21
+ :detail,
22
+ :source,
21
23
  :message
22
24
 
23
25
  def initialize(**args)
24
- self.api_version = configuration.api_version
25
- self.api_error_documentation_url = configuration.api_error_documentation_url
26
- self.knowledgebase_url = configuration.knowledgebase_url
27
-
28
26
  args.each do |variable, value|
29
- send("#{variable}=", value)
27
+ public_send("#{variable}=", value)
30
28
  end
31
29
  end
32
30
 
33
- def code
34
- @code || ErrorCodeDirectory.lookup(self.class.name)
31
+ def as_json(_options = {})
32
+ {
33
+ errors: [
34
+ {
35
+ id: id,
36
+ links: {
37
+ about: external_documentation_url,
38
+ documentation: developer_documentation_url,
39
+ },
40
+ status: http_status,
41
+ code: code,
42
+ title: title,
43
+ detail: detail,
44
+ source: source,
45
+ },
46
+ ],
47
+ }
35
48
  end
36
49
 
37
- def knowledgebase_article_id
38
- @knowledgebase_article_id || KnowledgebaseIdDirectory.lookup(self.class.name)
50
+ def to_json(_options = {})
51
+ JSON.dump(as_json)
39
52
  end
40
53
 
41
- def developer_documentation_uri
42
- "#{api_error_documentation_url}/#{code}?version=#{api_version}"
54
+ def id
55
+ @id ||= SecureRandom.uuid
43
56
  end
44
57
 
45
- def customer_support_uri
46
- "#{knowledgebase_url}/#{knowledgebase_article_id}"
58
+ def external_documentation_url
59
+ @external_documentation_url ||= configuration.external_documentation_urls[code]
47
60
  end
48
61
 
49
- def to_json(_options = {})
50
- JSON.dump(as_json)
62
+ def developer_documentation_url
63
+ @developer_documentation_url ||= configuration.developer_documentation_urls[code]
64
+ end
65
+
66
+ def http_status
67
+ @http_status ||= 500
68
+ end
69
+
70
+ alias_method :status, :http_status
71
+
72
+ def code
73
+ @code ||= HumanError::Utilities::String.
74
+ underscore(self.class.name).
75
+ gsub(%r{\A[^/]+/}, '').
76
+ gsub(%r{/}, '.')
77
+ end
78
+
79
+ def title
80
+ @title ||= self.class.name
81
+ end
82
+
83
+ def detail
84
+ @detail ||= 'The server encountered an error.'
85
+ end
86
+
87
+ def source
88
+ @source ||= {}
51
89
  end
52
90
 
53
91
  def message
@@ -55,13 +93,7 @@ module Error
55
93
  end
56
94
 
57
95
  def to_s
58
- @message || developer_message
59
- rescue NoMethodError
60
- super
61
- end
62
-
63
- def developer_message
64
- fail NoMethodError, 'This method must be implemented in a subclass'
96
+ @message || detail
65
97
  end
66
98
 
67
99
  def self.included(base)
@@ -1,9 +1,9 @@
1
- require 'human_error/errors/request_error'
2
1
  require 'human_error/errors/authentication_error'
3
2
 
4
3
  class HumanError
5
4
  module Errors
6
- class DuplicateAuthenticationError < RequestError
5
+ class DuplicateAuthenticationError < RuntimeError
6
+ include Error
7
7
  include AuthenticationError
8
8
 
9
9
  attr_accessor :provider,
@@ -14,23 +14,23 @@ class DuplicateAuthenticationError < RequestError
14
14
  409
15
15
  end
16
16
 
17
- def developer_message
17
+ def title
18
+ 'Duplicate Authentication'
19
+ end
20
+
21
+ def detail
18
22
  'The authentication you attempted to register has already been registered by ' \
19
23
  'another user. We do not currently support allowing multiple users to be connected ' \
20
24
  'to the same authentication.'
21
25
  end
22
26
 
23
- def developer_details
27
+ def source
24
28
  {
25
29
  'provider' => provider,
26
30
  'provider_user_id' => provider_user_id,
27
31
  'user_id' => user_id,
28
32
  }
29
33
  end
30
-
31
- def friendly_message
32
- "Sorry! Someone else has already registered this #{provider} login."
33
- end
34
34
  end
35
35
  end
36
36
  end
@@ -1,9 +1,9 @@
1
- require 'human_error/errors/request_error'
2
1
  require 'human_error/errors/authentication_error'
3
2
 
4
3
  class HumanError
5
4
  module Errors
6
- class InvalidTokenError < RequestError
5
+ class InvalidTokenError < RuntimeError
6
+ include Error
7
7
  include AuthenticationError
8
8
 
9
9
  attr_accessor :authentication_token
@@ -12,18 +12,18 @@ class InvalidTokenError < RequestError
12
12
  401
13
13
  end
14
14
 
15
- def developer_message
15
+ def title
16
+ 'Invalid Token'
17
+ end
18
+
19
+ def detail
16
20
  'The token you attempted to use for this request is invalid for this resource. ' \
17
21
  'Please double-check and try again.'
18
22
  end
19
23
 
20
- def developer_details
24
+ def source
21
25
  { token: '[FILTERED]' }
22
26
  end
23
-
24
- def friendly_message
25
- 'Sorry! You are not authorized to view this.'
26
- end
27
27
  end
28
28
  end
29
29
  end
@@ -1,9 +1,9 @@
1
- require 'human_error/errors/request_error'
2
1
  require 'human_error/errors/authentication_error'
3
2
 
4
3
  class HumanError
5
4
  module Errors
6
- class InvalidUsernameOrPasswordError < RequestError
5
+ class InvalidUsernameOrPasswordError < RuntimeError
6
+ include Error
7
7
  include AuthenticationError
8
8
 
9
9
  attr_accessor :username
@@ -12,18 +12,18 @@ class InvalidUsernameOrPasswordError < RequestError
12
12
  401
13
13
  end
14
14
 
15
- def developer_message
15
+ def title
16
+ 'Invalid Username/Password'
17
+ end
18
+
19
+ def detail
16
20
  'Either the username or password passed in or this request is invalid. Please ' \
17
21
  'double-check and try again.'
18
22
  end
19
23
 
20
- def developer_details
24
+ def source
21
25
  { username: username, password: '[FILTERED]' }
22
26
  end
23
-
24
- def friendly_message
25
- 'Either your email or password is incorrect. Please double-check and try again.'
26
- end
27
27
  end
28
28
  end
29
29
  end
@@ -1,9 +1,9 @@
1
1
  require 'human_error/errors/crud_error'
2
- require 'human_error/errors/request_error'
3
2
 
4
3
  class HumanError
5
4
  module Errors
6
- class AssociationError < RequestError
5
+ class AssociationError < RuntimeError
6
+ include Error
7
7
  include CrudError
8
8
 
9
9
  attr_accessor :association_name,
@@ -33,22 +33,21 @@ class AssociationError < RequestError
33
33
  422
34
34
  end
35
35
 
36
- def developer_message
36
+ def title
37
+ 'Association Error'
38
+ end
39
+
40
+ def detail
37
41
  "The #{association_name} that you attempted to associate with " \
38
42
  "the #{resource_name} was not valid."
39
43
  end
40
44
 
41
- def developer_details
45
+ def source
42
46
  {
43
47
  resource_name => attributes,
44
48
  "#{association_name} id" => association_id,
45
49
  }
46
50
  end
47
-
48
- def friendly_message
49
- "Sorry! There was a problem when we tried to set the #{association_name} on " \
50
- "that #{resource_name}."
51
- end
52
51
  end
53
52
  end
54
53
  end
@@ -1,9 +1,9 @@
1
1
  require 'human_error/errors/crud_error'
2
- require 'human_error/errors/request_error'
3
2
 
4
3
  class HumanError
5
4
  module Errors
6
- class ResourceNotFoundError < RequestError
5
+ class ResourceNotFoundError < RuntimeError
6
+ include Error
7
7
  include CrudError
8
8
 
9
9
  def self.convert(original_error, overrides = {})
@@ -30,19 +30,19 @@ class ResourceNotFoundError < RequestError
30
30
  404
31
31
  end
32
32
 
33
- def developer_message
33
+ def title
34
+ 'Resource Not Found'
35
+ end
36
+
37
+ def detail
34
38
  "The #{resource_name} you attempted to #{action} for this request is either " \
35
39
  'not authorized for the authenticated user or does not exist.'
36
40
  end
37
41
 
38
- def developer_details
42
+ def source
39
43
  { "#{resource_name_underscored}_id" => resource_id }
40
44
  end
41
45
 
42
- def friendly_message
43
- "Sorry! The #{resource_name} you tried to #{action} does not exist."
44
- end
45
-
46
46
  def action
47
47
  @action || 'access'
48
48
  end
@@ -1,9 +1,9 @@
1
1
  require 'human_error/errors/crud_error'
2
- require 'human_error/errors/request_error'
3
2
 
4
3
  class HumanError
5
4
  module Errors
6
- class ResourcePersistenceError < RequestError
5
+ class ResourcePersistenceError < RuntimeError
6
+ include Error
7
7
  include CrudError
8
8
 
9
9
  attr_accessor :errors,
@@ -29,21 +29,21 @@ class ResourcePersistenceError < RequestError
29
29
  422
30
30
  end
31
31
 
32
- def developer_message
32
+ def title
33
+ 'Resource Persistence Error'
34
+ end
35
+
36
+ def detail
33
37
  "One or more of the attributes on the #{resource_name} you attempted " \
34
38
  "to #{action} is invalid."
35
39
  end
36
40
 
37
- def developer_details
41
+ def source
38
42
  {
39
43
  'errors' => errors,
40
44
  'attributes' => attributes,
41
45
  }
42
46
  end
43
-
44
- def friendly_message
45
- "Sorry! We had a problem when tried to #{action} that #{resource_name}."
46
- end
47
47
  end
48
48
  end
49
49
  end