glia-errors 0.7.0 → 0.11.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f938565ae0608918f0808c950e1d2621f7d8ef2d347cb9146a37fc1af461a519
4
- data.tar.gz: 7d8d9672987fe83c77884532e052adee18e3e980462176b6482dd1a41fb755ec
3
+ metadata.gz: c39d2869c04082f96dec69d927b471a4ea9a94444cd1b1fefa27c1927599f6d6
4
+ data.tar.gz: f7858bf6b262b96e3dbd8c2463c6d62c299d9adb6a911b91cb0aa2c814196bdc
5
5
  SHA512:
6
- metadata.gz: f09300cb765f7ce0fe8d57cce7f8259303acd5dec9ffd61a36307ba0afb9d89c17ca95eb8010526a442ab0028da8fc9c61821f7fbe3cd5727c5d0df2a33e3821
7
- data.tar.gz: 8208ee1f6c9e00975df486a1d8fb3f1457f38a576970f15485191f9639340b0ff5247dbcf527f2474ef1f0696ff519994985e9917c91949c33c9e6bcbc9a3c42
6
+ metadata.gz: 2e01e99725306a950aa584ca37c00535f8a9bdcf1194e01961e325eb24393010df7afefadcd8e681536cc23c4594afec98e5622f4650b15c216a9e80ecce9149
7
+ data.tar.gz: 0626d5fc71db27e69180411f3bf77fb436357d1ff3b12019646115cf09f129edabd0faf40a0d8b704c023739f804e4b07de61768f852a8221d35385e04c16335
data/glia-errors.gemspec CHANGED
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'glia-errors'
8
- spec.version = '0.7.0'
8
+ spec.version = '0.11.1'
9
9
  spec.authors = ['Glia TechMovers']
10
10
  spec.email = ['techmovers@glia.com']
11
11
 
data/lib/glia/errors.rb CHANGED
@@ -12,7 +12,7 @@ module Glia
12
12
  def self.from_dry_validation_result(result, custom_error_map = {})
13
13
  dry_validation_version = Gem.loaded_specs['dry-validation'].version
14
14
  if dry_validation_version < Gem::Version.new('1.0')
15
- Mapper.from_dry_validation_result(result.output, result.messages, custom_error_map)
15
+ Mapper.from_dry_validation_result(result.output, result.errors, custom_error_map)
16
16
  elsif dry_validation_version <= Gem::Version.new('1.6')
17
17
  Mapper.from_dry_validation_result(result.to_h, result.errors.to_h, custom_error_map)
18
18
  else
@@ -5,6 +5,12 @@ module Glia
5
5
  # rubocop:disable Style/Documentation
6
6
  class InputValidationError < Error
7
7
  def initialize(error_details:, message: nil)
8
+ raise ArgumentError, 'At least 1 error detail is required' if error_details.keys.count.zero?
9
+
10
+ error_details.each_value do |value|
11
+ raise ArgumentError, 'error_details values must be lists' unless value.is_a?(Array)
12
+ end
13
+
8
14
  super(
9
15
  type: INPUT_VALIDATION_ERROR,
10
16
  ref: create_ref(INPUT_VALIDATION_ERROR),
@@ -19,7 +25,7 @@ module Glia
19
25
  super(
20
26
  type: INVALID_NUMBER_ERROR,
21
27
  ref: create_ref(INVALID_NUMBER_ERROR),
22
- message: message || "#{humanize(field)} value is invalid"
28
+ message: message || "#{Naming.humanize(field)} value is invalid"
23
29
  )
24
30
  end
25
31
  end
@@ -29,7 +35,7 @@ module Glia
29
35
  super(
30
36
  type: INVALID_VALUE_ERROR,
31
37
  ref: create_ref(INVALID_VALUE_ERROR),
32
- message: message || "#{humanize(field)} value is invalid"
38
+ message: message || "#{Naming.humanize(field)} value is invalid"
33
39
  )
34
40
  end
35
41
  end
@@ -39,7 +45,7 @@ module Glia
39
45
  super(
40
46
  type: INVALID_LENGTH_ERROR,
41
47
  ref: create_ref(INVALID_LENGTH_ERROR),
42
- message: message || "#{humanize(field)} length is invalid"
48
+ message: message || "#{Naming.humanize(field)} length is invalid"
43
49
  )
44
50
  end
45
51
  end
@@ -54,13 +60,34 @@ module Glia
54
60
 
55
61
  def initialize(field:, format: nil, message: nil)
56
62
  default_message =
57
- format ? "has invalid format, required format is #{format}" : 'has invalid format'
63
+ if format
64
+ "has invalid format, required format is #{humanize_format(format)}"
65
+ else
66
+ 'has invalid format'
67
+ end
58
68
  super(
59
69
  type: INVALID_FORMAT_ERROR,
60
70
  ref: create_ref(INVALID_FORMAT_ERROR),
61
- message: message || "#{humanize(field)} #{default_message}"
71
+ message: message || "#{Naming.humanize(field)} #{default_message}"
62
72
  )
63
73
  end
74
+
75
+ private
76
+
77
+ def humanize_format(format)
78
+ case format
79
+ when Formats::DATE
80
+ 'ISO-8601 date'
81
+ when Formats::TIME
82
+ 'ISO-8601 time'
83
+ when Formats::DATE_TIME
84
+ 'ISO-8601 date and time'
85
+ when Formats::UUID
86
+ 'UUID'
87
+ else
88
+ raise 'Unexpected InvalidFormatError format'
89
+ end
90
+ end
64
91
  end
65
92
 
66
93
  class InvalidTypeError < Error
@@ -77,7 +104,7 @@ module Glia
77
104
  super(
78
105
  type: INVALID_TYPE_ERROR,
79
106
  ref: create_ref(INVALID_TYPE_ERROR),
80
- message: message || "#{humanize(field)} must be of type #{type}",
107
+ message: message || "#{Naming.humanize(field)} must be of type #{type}",
81
108
  error_details: { type: type }
82
109
  )
83
110
  end
@@ -88,29 +115,34 @@ module Glia
88
115
  super(
89
116
  type: MISSING_VALUE_ERROR,
90
117
  ref: create_ref(MISSING_VALUE_ERROR),
91
- message: message || "#{humanize(field)} is missing"
118
+ message: message || "#{Naming.humanize(field)} is missing"
92
119
  )
93
120
  end
94
121
  end
95
122
 
96
123
  class UnknownError < Error
97
- def initialize(field:, message: nil)
124
+ def initialize(field: nil, message: nil)
98
125
  super(
99
126
  type: UNKNOWN_ERROR,
100
127
  ref: create_ref(UNKNOWN_ERROR),
101
- message: message || "#{humanize(field)} validation failed with unknown error"
128
+ message:
129
+ if field
130
+ message || "#{Naming.humanize(field)} validation failed with unknown error"
131
+ else
132
+ message || 'Failed with unknown error'
133
+ end
102
134
  )
103
135
  end
104
136
  end
105
137
 
106
138
  class ResourceNotFoundError < Error
107
139
  def initialize(resource:, message: nil)
108
- assert_snake_case(resource)
140
+ Naming.assert_snake_case(resource)
109
141
 
110
142
  super(
111
143
  type: RESOURCE_NOT_FOUND_ERROR,
112
144
  ref: create_ref(RESOURCE_NOT_FOUND_ERROR),
113
- message: message || "#{humanize(resource)} not found",
145
+ message: message || "#{Naming.humanize(resource)} not found",
114
146
  error_details: { resource: resource }
115
147
  )
116
148
  end
@@ -118,12 +150,12 @@ module Glia
118
150
 
119
151
  class NotVerifiedError < Error
120
152
  def initialize(resource:, message: nil)
121
- assert_snake_case(resource)
153
+ Naming.assert_snake_case(resource)
122
154
 
123
155
  super(
124
156
  type: NOT_VERIFIED_ERROR,
125
157
  ref: create_ref(NOT_VERIFIED_ERROR),
126
- message: message || "#{humanize(resource)} is not verified",
158
+ message: message || "#{Naming.humanize(resource)} is not verified",
127
159
  error_details: { resource: resource }
128
160
  )
129
161
  end
@@ -131,30 +163,30 @@ module Glia
131
163
 
132
164
  class RemainingAssociationError < Error
133
165
  def initialize(resource:, associated_resource:, message: nil)
134
- assert_snake_case(resource)
135
- assert_snake_case(associated_resource)
166
+ Naming.assert_snake_case(resource)
167
+ Naming.assert_snake_case(associated_resource)
136
168
 
137
169
  default_message =
138
170
  "cannot be modified/deleted because it is associated to one or more #{
139
- humanize(associated_resource)
171
+ Naming.humanize(associated_resource)
140
172
  }(s)"
141
173
  super(
142
174
  type: REMAINING_ASSOCIATION_ERROR,
143
175
  ref: create_ref(REMAINING_ASSOCIATION_ERROR),
144
- message: message || "#{humanize(resource)} #{default_message}",
176
+ message: message || "#{Naming.humanize(resource)} #{default_message}",
145
177
  error_details: { resource: resource, associated_resource: associated_resource }
146
178
  )
147
179
  end
148
180
  end
149
181
 
150
- class LimitExceededError < Error
182
+ class ResourceLimitExceededError < Error
151
183
  def initialize(resource:, max:, message: nil)
152
- assert_snake_case(resource)
184
+ Naming.assert_snake_case(resource)
153
185
 
154
186
  super(
155
187
  type: LIMIT_EXCEEDED_ERROR,
156
188
  ref: create_ref(LIMIT_EXCEEDED_ERROR),
157
- message: message || "#{humanize(resource)} count must not exceed #{max}",
189
+ message: message || "#{Naming.humanize(resource)} count must not exceed #{max}",
158
190
  error_details: { resource: resource, max: max }
159
191
  )
160
192
  end
@@ -162,12 +194,12 @@ module Glia
162
194
 
163
195
  class ResourceAlreadyExistsError < Error
164
196
  def initialize(resource:, message: nil)
165
- assert_snake_case(resource)
197
+ Naming.assert_snake_case(resource)
166
198
 
167
199
  super(
168
200
  type: RESOURCE_ALREADY_EXISTS_ERROR,
169
201
  ref: create_ref(RESOURCE_ALREADY_EXISTS_ERROR),
170
- message: message || "#{humanize(resource)} already exists",
202
+ message: message || "#{Naming.humanize(resource)} already exists",
171
203
  error_details: { resource: resource }
172
204
  )
173
205
  end
@@ -175,13 +207,13 @@ module Glia
175
207
 
176
208
  class InvalidResourceStateError < Error
177
209
  def initialize(resource:, state:, message: nil)
178
- assert_snake_case(resource)
179
- assert_snake_case(state)
210
+ Naming.assert_snake_case(resource)
211
+ Naming.assert_snake_case(state)
180
212
 
181
213
  super(
182
214
  type: INVALID_RESOURCE_STATE_ERROR,
183
215
  ref: create_ref(INVALID_RESOURCE_STATE_ERROR),
184
- message: message || "#{humanize(resource)} is in invalid state: #{state}",
216
+ message: message || "#{Naming.humanize(resource)} is in invalid state: #{state}",
185
217
  error_details: { resource: resource, state: state }
186
218
  )
187
219
  end
@@ -206,6 +238,115 @@ module Glia
206
238
  )
207
239
  end
208
240
  end
241
+
242
+ class RouteNotFoundError < Error
243
+ def initialize(message: nil)
244
+ super(
245
+ type: ROUTE_NOT_FOUND_ERROR,
246
+ ref: create_ref(ROUTE_NOT_FOUND_ERROR),
247
+ message: message || 'Route not found'
248
+ )
249
+ end
250
+ end
251
+
252
+ class MalformedInputError < Error
253
+ def initialize(message: nil)
254
+ super(
255
+ type: MALFORMED_INPUT_ERROR,
256
+ ref: create_ref(MALFORMED_INPUT_ERROR),
257
+ message: message || 'Request is malformed'
258
+ )
259
+ end
260
+ end
261
+
262
+ class CarrierError < Error
263
+ def initialize(message: nil)
264
+ super(
265
+ type: CARRIER_ERROR,
266
+ ref: create_ref(CARRIER_ERROR),
267
+ message: message || 'Downstream carrier issue occurred'
268
+ )
269
+ end
270
+ end
271
+
272
+ class GeographicPermissionError < Error
273
+ def initialize(message: nil)
274
+ super(
275
+ type: GEOGRAPHIC_PERMISSION_ERROR,
276
+ ref: create_ref(GEOGRAPHIC_PERMISSION_ERROR),
277
+ message: message || 'Insufficient permissions for geographic region'
278
+ )
279
+ end
280
+ end
281
+
282
+ class MessageBlockedError < Error
283
+ def initialize(message: nil)
284
+ super(
285
+ type: MESSAGE_BLOCKED_ERROR,
286
+ ref: create_ref(MESSAGE_BLOCKED_ERROR),
287
+ message: message || 'Message blocked or filtered'
288
+ )
289
+ end
290
+ end
291
+
292
+ class TelephonyProviderRateLimitExceededError < Error
293
+ def initialize(message: nil)
294
+ super(
295
+ type: TELEPHONY_PROVIDER_RATE_LIMIT_EXCEEDED_ERROR,
296
+ ref: create_ref(TELEPHONY_PROVIDER_RATE_LIMIT_EXCEEDED_ERROR),
297
+ message: message || 'Telephony provider message send rate limit exceeded'
298
+ )
299
+ end
300
+ end
301
+
302
+ class TelephonyProviderQueueLimitExceededError < Error
303
+ def initialize(message: nil)
304
+ super(
305
+ type: TELEPHONY_PROVIDER_QUEUE_LIMIT_EXCEEDED_ERROR,
306
+ ref: create_ref(TELEPHONY_PROVIDER_QUEUE_LIMIT_EXCEEDED_ERROR),
307
+ message: message || 'Telephony provider message send queue is full'
308
+ )
309
+ end
310
+ end
311
+
312
+ class TwilioMessagingServiceConfigurationError < Error
313
+ def initialize(message: nil)
314
+ super(
315
+ type: TWILIO_MESSAGING_SERVICE_CONFIGURATION_ERROR,
316
+ ref: create_ref(TWILIO_MESSAGING_SERVICE_CONFIGURATION_ERROR),
317
+ message: message || 'Invalid Twilio Messaging Service configuration'
318
+ )
319
+ end
320
+ end
321
+
322
+ class UnreachableDestinationError < Error
323
+ def initialize(message: nil)
324
+ super(
325
+ type: UNREACHABLE_DESTINATION_ERROR,
326
+ ref: create_ref(UNREACHABLE_DESTINATION_ERROR),
327
+ message: message || 'Destination is unreachable'
328
+ )
329
+ end
330
+ end
331
+
332
+ class HeadersValidationError < Error
333
+ def initialize(error_details:, message: nil)
334
+ raise ArgumentError, 'At least 1 error detail is required' if error_details.keys.count.zero?
335
+
336
+ error_details.each_value do |value|
337
+ raise ArgumentError, 'error_details values must be lists' unless value.is_a?(Array)
338
+ end
339
+
340
+ error_details.each_key { |key| Naming.assert_header(key) }
341
+
342
+ super(
343
+ type: HEADERS_VALIDATION_ERROR,
344
+ ref: create_ref(HEADERS_VALIDATION_ERROR),
345
+ message: message || 'Headers are invalid',
346
+ error_details: error_details
347
+ )
348
+ end
349
+ end
209
350
  # rubocop:enable Style/Documentation
210
351
  end
211
352
  end
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './naming'
4
+
3
5
  module Glia
4
6
  module Errors
5
7
  # Base error
6
8
  class Error
7
- SNAKE_CASE_REGEX = /^[a-z0-9]+(_[a-z0-9]+)*$/.freeze
8
9
  attr_reader :type, :ref, :message, :error_details
9
10
 
10
11
  def initialize(type:, ref:, message: nil, error_details: nil)
@@ -43,18 +44,6 @@ module Glia
43
44
  [TrueClass, FalseClass, String, Integer, Float, Symbol].include?(details.class)
44
45
  end
45
46
 
46
- # Converts from camel_case to capitalized more human readable value
47
- # first_name => "First name"
48
- def humanize(value)
49
- value.to_s.capitalize.gsub('_', ' ')
50
- end
51
-
52
- def assert_snake_case(value)
53
- return if value.to_s.match(SNAKE_CASE_REGEX)
54
-
55
- raise ArgumentError, "Expected '#{value}' to be in snake case"
56
- end
57
-
58
47
  def create_ref(type)
59
48
  fragment = type.gsub('_', '-')
60
49
  "https://docs.glia.com/glia-dev/reference/errors##{fragment}"
@@ -19,6 +19,16 @@ module Glia
19
19
  INVALID_RESOURCE_STATE_ERROR = 'invalid_resource_state_error'
20
20
  AUTHORIZATION_ERROR = 'authorization_error'
21
21
  RECIPIENT_OPTED_OUT_ERROR = 'recipient_opted_out_error'
22
+ ROUTE_NOT_FOUND_ERROR = 'route_not_found_error'
23
+ MALFORMED_INPUT_ERROR = 'malformed_input_error'
24
+ CARRIER_ERROR = 'carrier_error'
25
+ GEOGRAPHIC_PERMISSION_ERROR = 'geographic_permission_error'
26
+ MESSAGE_BLOCKED_ERROR = 'message_blocked_error'
27
+ TELEPHONY_PROVIDER_RATE_LIMIT_EXCEEDED_ERROR = 'telephony_provider_rate_limit_exceeded_error'
28
+ TELEPHONY_PROVIDER_QUEUE_LIMIT_EXCEEDED_ERROR = 'telephony_provider_queue_limit_exceeded_error'
29
+ TWILIO_MESSAGING_SERVICE_CONFIGURATION_ERROR = 'twilio_messaging_service_configuration_error'
30
+ UNREACHABLE_DESTINATION_ERROR = 'unreachable_destination_error'
31
+ HEADERS_VALIDATION_ERROR = 'headers_validation_error'
22
32
 
23
33
  # Server errors
24
34
  INTERNAL_SERVER_ERROR = 'internal_server_error'
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glia
4
+ module Errors
5
+ # Utilities for variable and resouce names
6
+ module Naming
7
+ # Converts from camel_case to more human readable value
8
+ # first_name => "First name"
9
+ # site_id => "Site ID"
10
+ def self.humanize(value)
11
+ result = value.to_s.split('_').map { |word| upcase_if_abbreviation(word) }.join(' ')
12
+
13
+ upcase_first(result)
14
+ end
15
+
16
+ ABBREVIATIONS = %w[id uuid saml sip sms mms uri url].freeze
17
+ PLURAL_ABBREVIATIONS = %w[ids uuids uris urls].freeze
18
+
19
+ private_class_method def self.upcase_if_abbreviation(value)
20
+ if ABBREVIATIONS.include?(value)
21
+ value.upcase
22
+ elsif PLURAL_ABBREVIATIONS.include?(value)
23
+ value[0..-2].upcase.concat('s')
24
+ else
25
+ value
26
+ end
27
+ end
28
+
29
+ private_class_method def self.upcase_first(value)
30
+ value[0].upcase.concat(value[1..-1])
31
+ end
32
+
33
+ SNAKE_CASE_REGEX = /\A[a-z0-9]+(_[a-z0-9]+)*\z/.freeze
34
+
35
+ def self.assert_snake_case(value)
36
+ return if value.to_s.match(SNAKE_CASE_REGEX)
37
+
38
+ raise ArgumentError, "Expected '#{value}' to be in snake case"
39
+ end
40
+
41
+ HEADER_REGEX = /\A[A-Z0-9]+[a-z0-9]*(-[A-Z0-9]+[a-zz0-9]*)*\z/.freeze
42
+
43
+ def self.assert_header(value)
44
+ return if value.to_s.match(HEADER_REGEX)
45
+
46
+ raise ArgumentError, "Expected '#{value}' to be a valid header"
47
+ end
48
+ end
49
+ end
50
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glia-errors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Glia TechMovers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-05 00:00:00.000000000 Z
11
+ date: 2021-05-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: ''
14
14
  email:
@@ -37,6 +37,7 @@ files:
37
37
  - lib/glia/errors/error.rb
38
38
  - lib/glia/errors/error_types.rb
39
39
  - lib/glia/errors/mapper.rb
40
+ - lib/glia/errors/naming.rb
40
41
  - lib/glia/errors/server_errors.rb
41
42
  homepage:
42
43
  licenses: