contentful-management 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/contentful/management/error.rb +186 -28
  4. data/lib/contentful/management/response.rb +5 -4
  5. data/lib/contentful/management/version.rb +1 -1
  6. data/spec/fixtures/json_responses/400_details_errors_object.json +14 -0
  7. data/spec/fixtures/json_responses/400_details_errors_string.json +12 -0
  8. data/spec/fixtures/json_responses/400_details_string.json +8 -0
  9. data/spec/fixtures/json_responses/403_reasons.json +13 -0
  10. data/spec/fixtures/json_responses/404_details_string.json +8 -0
  11. data/spec/fixtures/json_responses/404_id.json +11 -0
  12. data/spec/fixtures/json_responses/404_type.json +10 -0
  13. data/spec/fixtures/json_responses/422_details.json +22 -0
  14. data/spec/fixtures/json_responses/default_400.json +7 -0
  15. data/spec/fixtures/json_responses/default_401.json +7 -0
  16. data/spec/fixtures/json_responses/default_403.json +7 -0
  17. data/spec/fixtures/json_responses/default_404.json +7 -0
  18. data/spec/fixtures/json_responses/default_409.json +7 -0
  19. data/spec/fixtures/json_responses/default_422.json +7 -0
  20. data/spec/fixtures/json_responses/default_429.json +7 -0
  21. data/spec/fixtures/json_responses/default_500.json +7 -0
  22. data/spec/fixtures/json_responses/default_502.json +7 -0
  23. data/spec/fixtures/json_responses/default_503.json +7 -0
  24. data/spec/fixtures/json_responses/not_found.json +13 -0
  25. data/spec/fixtures/json_responses/other_error.json +9 -0
  26. data/spec/fixtures/json_responses/other_error_no_details.json +8 -0
  27. data/spec/fixtures/json_responses/other_error_no_message.json +8 -0
  28. data/spec/fixtures/json_responses/other_error_no_request_id.json +8 -0
  29. data/spec/fixtures/json_responses/other_error_nothing.json +6 -0
  30. data/spec/fixtures/json_responses/unparsable.json +13 -0
  31. data/spec/fixtures/vcr_cassettes/asset/destroy_published.yml +1 -1
  32. data/spec/fixtures/vcr_cassettes/content_type/destroy_activated.yml +1 -1
  33. data/spec/lib/contentful/management/asset_spec.rb +16 -3
  34. data/spec/lib/contentful/management/content_type_spec.rb +10 -2
  35. data/spec/lib/contentful/management/entry_spec.rb +25 -5
  36. data/spec/lib/contentful/management/error_class_spec.rb +369 -0
  37. data/spec/support/json_responses.rb +18 -0
  38. metadata +56 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb5ebdbf246240b9eeaf315eb01fd8950cce67a4
4
- data.tar.gz: bb20ddab6ba4bd2ee7353e2d9c5eb841027955b3
3
+ metadata.gz: 1c4ec83b46a51eb77dec3b180f0e016a8e0955f4
4
+ data.tar.gz: f399a8297f69fa59fc7bc2d84726b599a90293f2
5
5
  SHA512:
6
- metadata.gz: a365765f6243235b658fc34e692939687e44d7d679fde7c3a93417c64d63bb0421d5fe128d9d5df831d3370d055ae7ff7ca7530892bfdc7962a68d57b1cf1139
7
- data.tar.gz: 978075d563968f6cca95c76d0a96ab15fb4068c13e9f7f1f8c395a93ab9ae04103760647ce21c0c4f0aaf672eb7daa1a63699b3bf032b1ff1f53798daabf8b2c
6
+ metadata.gz: 2c7b7cc83f3a25c245f7fc5903fd6acc096f6d7a8225c9cb10676d728c2f406ff82c6db899220daaf7a7622bc5c82bc7081db217ee5c0a3ea6d4441322fb9b7a
7
+ data.tar.gz: 213e66c26d64ba6045f56aec03c4520e3bfa6ef5477cea6007790b56cda5e00af1a6578b3e2665f0d4f352487278ea457cb6b0bfaae0f984b58527f80e968034
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Master
4
4
 
5
+ ## 1.10.0
6
+ ### Added
7
+ * Added better error messages for all possible API errors [#95](https://github.com/contentful/contentful-management.rb/issues/95)
8
+
5
9
  ## 1.9.0
6
10
  ### Added
7
11
  * Added option to disable Content Type caching completely.
@@ -12,75 +12,233 @@ module Contentful
12
12
  message: response.error_message,
13
13
  details: response.raw.body.instance_variable_get(:@contents)
14
14
  }
15
- super @response.error_message
15
+ super best_available_message
16
16
  end
17
17
 
18
18
  # Shortcut for creating specialized error classes
19
19
  # USAGE rescue Contentful::Management::Error[404]
20
20
  def self.[](error_status_code)
21
- case error_status_code
22
- when 404
23
- NotFound
24
- when 400
25
- BadRequest
26
- when 403
27
- AccessDenied
28
- when 409
29
- Conflict
30
- when 401
31
- Unauthorized
32
- when 422
33
- UnprocessableEntity
34
- when 429
35
- RateLimitExceeded
36
- when 500
37
- ServerError
38
- when 503
39
- ServiceUnavailable
40
- else
41
- Error
42
- end
21
+ errors = {
22
+ 400 => BadRequest,
23
+ 401 => Unauthorized,
24
+ 403 => AccessDenied,
25
+ 404 => NotFound,
26
+ 409 => Conflict,
27
+ 422 => UnprocessableEntity,
28
+ 429 => RateLimitExceeded,
29
+ 500 => ServerError,
30
+ 502 => BadGateway,
31
+ 503 => ServiceUnavailable
32
+ }.freeze
33
+
34
+ errors.key?(error_status_code) ? errors[error_status_code] : Error
43
35
  end
44
- end
45
36
 
46
- # 404
47
- class NotFound < Error
37
+ protected
38
+
39
+ def default_error_message
40
+ "The following error was received: #{@response.raw.body}"
41
+ end
42
+
43
+ def handle_details(details)
44
+ details.to_s
45
+ end
46
+
47
+ def additional_info?
48
+ false
49
+ end
50
+
51
+ def additional_info
52
+ []
53
+ end
54
+
55
+ def best_available_message
56
+ error_message = [
57
+ "HTTP status code: #{@response.raw.status}"
58
+ ]
59
+
60
+ begin
61
+ response_json = @response.load_json
62
+ message = response_json.fetch('message', default_error_message)
63
+ details = response_json.fetch('details', nil)
64
+ request_id = response_json.fetch('requestId', nil)
65
+
66
+ error_message << "Message: #{message}"
67
+ error_message << "Details: #{handle_details(details)}" if details
68
+ error_message << "Request ID: #{request_id}" if request_id
69
+ rescue
70
+ error_message << "Message: #{default_error_message}"
71
+ end
72
+
73
+ error_message << additional_info if additional_info?
74
+
75
+ error_message.join("\n")
76
+ end
48
77
  end
49
78
 
50
79
  # 400
51
80
  class BadRequest < Error
81
+ protected
82
+
83
+ def default_error_message
84
+ 'The request was malformed or missing a required parameter.'
85
+ end
86
+
87
+ def handle_details(details)
88
+ return details if details.is_a?(String)
89
+
90
+ handle_detail = proc do |detail|
91
+ return detail if detail.is_a?(String)
92
+ detail.fetch('details', nil)
93
+ end
94
+
95
+ inner_details = details['errors'].map { |detail| handle_detail[detail] }.reject(&:nil?)
96
+ inner_details.join("\n\t")
97
+ end
98
+ end
99
+
100
+ # 401
101
+ class Unauthorized < Error
102
+ protected
103
+
104
+ def default_error_message
105
+ 'The authorization token was invalid.'
106
+ end
52
107
  end
53
108
 
54
109
  # 403
55
110
  class AccessDenied < Error
111
+ protected
112
+
113
+ def default_error_message
114
+ 'The specified token does not have access to the requested resource.'
115
+ end
116
+
117
+ def handle_details(details)
118
+ "\n\tReasons:\n\t\t#{details['reasons'].join("\n\t\t")}"
119
+ end
56
120
  end
57
121
 
58
- # 401
59
- class Unauthorized < Error
122
+ # 404
123
+ class NotFound < Error
124
+ protected
125
+
126
+ def default_error_message
127
+ 'The requested resource or endpoint could not be found.'
128
+ end
129
+
130
+ def handle_details(details)
131
+ return details if details.is_a?(String)
132
+
133
+ message = "The requested #{details['type']} could not be found."
134
+
135
+ resource_id = details.fetch('id', nil)
136
+ message += " ID: #{resource_id}." if resource_id
137
+
138
+ message
139
+ end
60
140
  end
61
141
 
62
142
  # 409
63
143
  class Conflict < Error
144
+ protected
145
+
146
+ def default_error_message
147
+ 'Version mismatch error. The version you specified was incorrect. This may be due to someone else editing the content.'
148
+ end
64
149
  end
65
150
 
66
151
  # 422
67
152
  class UnprocessableEntity < Error
153
+ protected
154
+
155
+ def default_error_message
156
+ 'The resource you sent in the body is invalid.'
157
+ end
158
+
159
+ def handle_error(error)
160
+ name = error['name']
161
+ path = error['path']
162
+ value = error['value']
163
+
164
+ "\t* Name: #{name} - Path: '#{path}' - Value: '#{value}'"
165
+ end
166
+
167
+ def handle_details(details)
168
+ errors = []
169
+ details['errors'].each do |error|
170
+ errors << handle_error(error)
171
+ end
172
+
173
+ "\n#{errors.join("\n")}"
174
+ end
68
175
  end
69
176
 
70
177
  # 429
71
178
  class RateLimitExceeded < Error
179
+ # Rate Limit Reset Header Key
180
+ RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'.freeze
181
+
182
+ def reset_time?
183
+ # rubocop:disable Style/DoubleNegation
184
+ !!reset_time
185
+ # rubocop:enable Style/DoubleNegation
186
+ end
187
+
188
+ # Time until next available request, in seconds.
189
+ def reset_time
190
+ @reset_time ||= @response.raw[RATE_LIMIT_RESET_HEADER_KEY]
191
+ end
192
+
193
+ protected
194
+
195
+ def additional_info?
196
+ reset_time?
197
+ end
198
+
199
+ def additional_info
200
+ ["Time until reset (seconds): #{reset_time}"]
201
+ end
202
+
203
+ def default_error_message
204
+ 'Rate limit exceeded. Too many requests.'
205
+ end
72
206
  end
73
207
 
74
208
  # 500
75
209
  class ServerError < Error
210
+ protected
211
+
212
+ def default_error_message
213
+ 'Internal server error.'
214
+ end
215
+ end
216
+
217
+ # 502
218
+ class BadGateway < Error
219
+ protected
220
+
221
+ def default_error_message
222
+ 'The requested space is hibernated.'
223
+ end
76
224
  end
77
225
 
78
226
  # 503
79
227
  class ServiceUnavailable < Error
228
+ protected
229
+
230
+ def default_error_message
231
+ 'Service unavailable.'
232
+ end
80
233
  end
81
234
 
82
235
  # Raised when response is no valid json
83
236
  class UnparsableJson < Error
237
+ protected
238
+
239
+ def default_error_message
240
+ @response.error_message
241
+ end
84
242
  end
85
243
 
86
244
  # Raised when response is not parsable as a Contentful::Management::Resource
@@ -41,6 +41,11 @@ module Contentful
41
41
  end
42
42
  end
43
43
 
44
+ # Returns the JSON body of the response
45
+ def load_json
46
+ MultiJson.load(unzip_response(raw))
47
+ end
48
+
44
49
  private
45
50
 
46
51
  def error_object?
@@ -87,10 +92,6 @@ module Contentful
87
92
  UnparsableJson.new(self)
88
93
  end
89
94
 
90
- def load_json
91
- MultiJson.load(unzip_response(raw))
92
- end
93
-
94
95
  def unzip_response(response)
95
96
  parsed_response = response.to_s
96
97
  if response.headers['Content-Encoding'].eql?('gzip')
@@ -3,6 +3,6 @@ module Contentful
3
3
  # Management Namespace
4
4
  module Management
5
5
  # Gem Version
6
- VERSION = '1.9.0'.freeze
6
+ VERSION = '1.10.0'.freeze
7
7
  end
8
8
  end
@@ -0,0 +1,14 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "BadRequest"
5
+ },
6
+ "details": {
7
+ "errors": [
8
+ {
9
+ "details": "some error"
10
+ }
11
+ ]
12
+ },
13
+ "requestId": "85f-351076632"
14
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "BadRequest"
5
+ },
6
+ "details": {
7
+ "errors": [
8
+ "some error"
9
+ ]
10
+ },
11
+ "requestId": "85f-351076632"
12
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "BadRequest"
5
+ },
6
+ "details": "some error",
7
+ "requestId": "85f-351076632"
8
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "AccessDenied"
5
+ },
6
+ "details": {
7
+ "reasons": [
8
+ "foo",
9
+ "bar"
10
+ ]
11
+ },
12
+ "requestId": "85f-351076632"
13
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "NotFound"
5
+ },
6
+ "details": "The resource could not be found",
7
+ "requestId": "85f-351076632"
8
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "NotFound"
5
+ },
6
+ "details": {
7
+ "type": "Asset",
8
+ "id": "foobar"
9
+ },
10
+ "requestId": "85f-351076632"
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "NotFound"
5
+ },
6
+ "details": {
7
+ "type": "Asset"
8
+ },
9
+ "requestId": "85f-351076632"
10
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "requestId": "044bb23356babe4f11a3f7f1e77c762a",
3
+ "message":"The resource you sent in the body is invalid.",
4
+ "details":{
5
+ "errors":[
6
+ {
7
+ "name":"taken",
8
+ "path":"display_code",
9
+ "value":"en-US"
10
+ },
11
+ {
12
+ "name":"fallback locale creates a loop",
13
+ "path":"fallback_code",
14
+ "value":"en-US"
15
+ }
16
+ ]
17
+ },
18
+ "sys":{
19
+ "type":"Error",
20
+ "id":"ValidationFailed"
21
+ }
22
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "BadRequest"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "Unauthorized"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "AccessDenied"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "NotFound"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "Conflict"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "UnprocessableEntity"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "RateLimitExceeded"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "ServerError"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "BadGateway"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "ServiceUnavailable"
5
+ },
6
+ "requestId": "85f-351076632"
7
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "NotFound"
5
+ },
6
+ "details": {
7
+ "type": "Entry",
8
+ "space": "cfexampleapi",
9
+ "id": "not found"
10
+ },
11
+ "message": "The resource could not be found.",
12
+ "requestId": "85f-351076632"
13
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "SomeError"
5
+ },
6
+ "details": "some text",
7
+ "message": "Some error occurred.",
8
+ "requestId": "85f-351076632"
9
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "SomeError"
5
+ },
6
+ "message": "Some error occurred.",
7
+ "requestId": "85f-351076632"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "SomeError"
5
+ },
6
+ "details": "some text",
7
+ "requestId": "85f-351076632"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "SomeError"
5
+ },
6
+ "details": "some text",
7
+ "message": "Some error occurred."
8
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "SomeError"
5
+ }
6
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "sys": {
3
+ "type": "Error",
4
+ "id": "NotFound"
5
+ },
6
+ "details": {
7
+ "type": "Entry",
8
+ "space": "cfexampleapi",
9
+ "id": "not found"
10
+ },
11
+ "message": "The resource could not be found.",
12
+ "requestId": "85f-351076632",
13
+ "missing": "braceright"
@@ -168,7 +168,7 @@ http_interactions:
168
168
  "type": "Error",
169
169
  "id": "BadRequest"
170
170
  },
171
- "message": "Cannot deleted published"
171
+ "message": "Cannot delete published"
172
172
  }
173
173
  http_version:
174
174
  recorded_at: Thu, 31 Jul 2014 07:28:33 GMT
@@ -157,7 +157,7 @@ http_interactions:
157
157
  "type": "Error",
158
158
  "id": "BadRequest"
159
159
  },
160
- "message": "Cannot deleted published"
160
+ "message": "Cannot delete published"
161
161
  }
162
162
  http_version:
163
163
  recorded_at: Thu, 31 Jul 2014 08:42:20 GMT
@@ -76,7 +76,12 @@ module Contentful
76
76
  vcr('asset/find_not_found') do
77
77
  result = subject.find(space_id, 'not_exist')
78
78
  expect(result).to be_kind_of Contentful::Management::NotFound
79
- expect(result.message).to eq 'The resource could not be found.'
79
+ message = [
80
+ "HTTP status code: 404 Not Found",
81
+ "Message: The resource could not be found.",
82
+ "Details: The requested Asset could not be found. ID: not_exist."
83
+ ].join("\n")
84
+ expect(result.message).to eq message
80
85
  end
81
86
  end
82
87
  end
@@ -86,7 +91,11 @@ module Contentful
86
91
  vcr('asset/destroy_published') do
87
92
  result = subject.find(space_id, 'r7o2iuDeSc4UmioOuoKq6').destroy
88
93
  expect(result).to be_kind_of Contentful::Management::BadRequest
89
- expect(result.message).to eq 'Cannot deleted published'
94
+ message = [
95
+ "HTTP status code: 400 Bad Request",
96
+ "Message: Cannot delete published"
97
+ ].join("\n")
98
+ expect(result.message).to eq message
90
99
  end
91
100
  end
92
101
  it 'returns true when asset is not published' do
@@ -112,7 +121,11 @@ module Contentful
112
121
  vcr('asset/unpublish_already_unpublished') do
113
122
  result = subject.find(space_id, asset_id_2).unpublish
114
123
  expect(result).to be_kind_of Contentful::Management::BadRequest
115
- expect(result.message).to eq 'Not published'
124
+ message = [
125
+ "HTTP status code: 400 Bad Request",
126
+ "Message: Not published"
127
+ ].join("\n")
128
+ expect(result.message).to eq message
116
129
  end
117
130
  end
118
131
  end
@@ -63,7 +63,11 @@ module Contentful
63
63
  vcr('content_type/destroy_activated') do
64
64
  result = subject.find(space_id, '66jvD8UhNKmWGk24KKq0EW').destroy
65
65
  expect(result).to be_kind_of Contentful::Management::BadRequest
66
- expect(result.message).to eq 'Cannot deleted published'
66
+ message = [
67
+ "HTTP status code: 400 Bad Request",
68
+ "Message: Cannot delete published"
69
+ ].join("\n")
70
+ expect(result.message).to eq message
67
71
  end
68
72
  end
69
73
 
@@ -129,7 +133,11 @@ module Contentful
129
133
  content_type = subject.find(space_id, deactivate_content)
130
134
  content_type.sys[:version] = -1
131
135
  result = content_type.deactivate
132
- expect(result.message).to eq 'Not published'
136
+ message = [
137
+ "HTTP status code: 400 Bad Request",
138
+ "Message: Not published"
139
+ ].join("\n")
140
+ expect(result.message).to eq message
133
141
  end
134
142
  end
135
143
  end
@@ -122,7 +122,11 @@ module Contentful
122
122
  vcr('entry/service_unavailable') do
123
123
  result = subject.find(space_id, 'not_exist')
124
124
  expect(result).to be_kind_of Contentful::Management::ServiceUnavailable
125
- expect(result.message).to eq 'Service Unavailable, contentful.com API seems to be down'
125
+ message = [
126
+ "HTTP status code: 503 Service Unavailable",
127
+ "Message: Service unavailable."
128
+ ].join("\n")
129
+ expect(result.message).to eq message
126
130
  end
127
131
  end
128
132
  end
@@ -132,7 +136,11 @@ module Contentful
132
136
  vcr('entry/destory_published') do
133
137
  result = subject.find(space_id, '3U7JqGuVzOWIimU40mKeem').destroy
134
138
  expect(result).to be_kind_of Contentful::Management::BadRequest
135
- expect(result.message).to eq 'Cannot deleted published'
139
+ message = [
140
+ "HTTP status code: 400 Bad Request",
141
+ "Message: Cannot deleted published"
142
+ ].join("\n")
143
+ expect(result.message).to eq message
136
144
  end
137
145
  end
138
146
  it 'returns true when entry is not published' do
@@ -158,7 +166,11 @@ module Contentful
158
166
  vcr('entry/unpublish_already_unpublished') do
159
167
  result = subject.find(space_id, entry_id).unpublish
160
168
  expect(result).to be_kind_of Contentful::Management::BadRequest
161
- expect(result.message).to eq 'Not published'
169
+ message = [
170
+ "HTTP status code: 400 Bad Request",
171
+ "Message: Not published"
172
+ ].join("\n")
173
+ expect(result.message).to eq message
162
174
  expect(result.error[:message]).to eq 'Not published'
163
175
  expect(result.error[:url]).to eq 'spaces/yr5m0jky5hsh/entries/4Rouux8SoUCKwkyCq2I0E0/published'
164
176
  expect(result.error[:details]).to eq "{\n \"sys\": {\n \"type\": \"Error\",\n \"id\": \"BadRequest\"\n },\n \"message\": \"Not published\"\n}\n"
@@ -224,7 +236,11 @@ module Contentful
224
236
  vcr('entry/unarchive_already_unarchived') do
225
237
  result = subject.find(space_id, entry_id).unarchive
226
238
  expect(result).to be_kind_of Contentful::Management::BadRequest
227
- expect(result.message).to eql 'Not archived'
239
+ message = [
240
+ "HTTP status code: 400 Bad Request",
241
+ "Message: Not archived"
242
+ ].join("\n")
243
+ expect(result.message).to eq message
228
244
  end
229
245
  end
230
246
  end
@@ -243,7 +259,11 @@ module Contentful
243
259
  vcr('entry/archive_published') do
244
260
  entry = subject.find(space_id, entry_id).archive
245
261
  expect(entry).to be_kind_of Contentful::Management::BadRequest
246
- expect(entry.message).to eql 'Cannot archive published'
262
+ message = [
263
+ "HTTP status code: 400 Bad Request",
264
+ "Message: Cannot archive published"
265
+ ].join("\n")
266
+ expect(entry.message).to eq message
247
267
  end
248
268
  end
249
269
  end
@@ -0,0 +1,369 @@
1
+ require 'spec_helper'
2
+
3
+ class MockRequest
4
+ def endpoint; end
5
+ end
6
+
7
+ describe Contentful::Management::Error do
8
+ let(:r) { Contentful::Management::Response.new raw_fixture('not_found', 404), MockRequest.new }
9
+
10
+ describe '#response' do
11
+ it 'returns the response the error has been initialized with' do
12
+ expect(Contentful::Management::Error.new(r).response).to be r
13
+ end
14
+ end
15
+
16
+ describe '#message' do
17
+ it 'returns the message found in the response json' do
18
+ message = "HTTP status code: 404\n"\
19
+ "Message: The resource could not be found.\n"\
20
+ "Details: {\"type\"=>\"Entry\", \"space\"=>\"cfexampleapi\", \"id\"=>\"not found\"}\n"\
21
+ "Request ID: 85f-351076632"
22
+ expect(Contentful::Management::Error.new(r).message).not_to be_nil
23
+ expect(Contentful::Management::Error.new(r).message).to eq message
24
+ end
25
+
26
+ describe 'message types' do
27
+ describe 'default messages' do
28
+ it '400' do
29
+ response = Contentful::Management::Response.new raw_fixture('default_400', 400), MockRequest.new
30
+ error = Contentful::Management::Error[response.raw.status].new(response)
31
+
32
+ message = "HTTP status code: 400\n"\
33
+ "Message: The request was malformed or missing a required parameter.\n"\
34
+ "Request ID: 85f-351076632"
35
+ expect(error.message).to eq message
36
+ end
37
+
38
+ it '401' do
39
+ response = Contentful::Management::Response.new raw_fixture('default_401', 401), MockRequest.new
40
+ error = Contentful::Management::Error[response.raw.status].new(response)
41
+
42
+ message = "HTTP status code: 401\n"\
43
+ "Message: The authorization token was invalid.\n"\
44
+ "Request ID: 85f-351076632"
45
+ expect(error.message).to eq message
46
+ end
47
+
48
+ it '403' do
49
+ response = Contentful::Management::Response.new raw_fixture('default_403', 403), MockRequest.new
50
+ error = Contentful::Management::Error[response.raw.status].new(response)
51
+
52
+ message = "HTTP status code: 403\n"\
53
+ "Message: The specified token does not have access to the requested resource.\n"\
54
+ "Request ID: 85f-351076632"
55
+ expect(error.message).to eq message
56
+ end
57
+
58
+ it '404' do
59
+ response = Contentful::Management::Response.new raw_fixture('default_404', 404), MockRequest.new
60
+ error = Contentful::Management::Error[response.raw.status].new(response)
61
+
62
+ message = "HTTP status code: 404\n"\
63
+ "Message: The requested resource or endpoint could not be found.\n"\
64
+ "Request ID: 85f-351076632"
65
+ expect(error.message).to eq message
66
+ end
67
+
68
+ it '409' do
69
+ response = Contentful::Management::Response.new raw_fixture('default_409', 409), MockRequest.new
70
+ error = Contentful::Management::Error[response.raw.status].new(response)
71
+
72
+ message = "HTTP status code: 409\n"\
73
+ "Message: Version mismatch error. The version you specified was incorrect. This may be due to someone else editing the content.\n"\
74
+ "Request ID: 85f-351076632"
75
+ expect(error.message).to eq message
76
+ end
77
+
78
+ it '422' do
79
+ response = Contentful::Management::Response.new raw_fixture('default_422', 422), MockRequest.new
80
+ error = Contentful::Management::Error[response.raw.status].new(response)
81
+
82
+ message = "HTTP status code: 422\n"\
83
+ "Message: The resource you sent in the body is invalid.\n"\
84
+ "Request ID: 85f-351076632"
85
+ expect(error.message).to eq message
86
+ end
87
+
88
+ it '429' do
89
+ response = Contentful::Management::Response.new raw_fixture('default_429', 429), MockRequest.new
90
+ error = Contentful::Management::Error[response.raw.status].new(response)
91
+
92
+ message = "HTTP status code: 429\n"\
93
+ "Message: Rate limit exceeded. Too many requests.\n"\
94
+ "Request ID: 85f-351076632"
95
+ expect(error.message).to eq message
96
+ end
97
+
98
+ it '500' do
99
+ response = Contentful::Management::Response.new raw_fixture('default_500', 500), MockRequest.new
100
+ error = Contentful::Management::Error[response.raw.status].new(response)
101
+
102
+ message = "HTTP status code: 500\n"\
103
+ "Message: Internal server error.\n"\
104
+ "Request ID: 85f-351076632"
105
+ expect(error.message).to eq message
106
+ end
107
+
108
+ it '502' do
109
+ response = Contentful::Management::Response.new raw_fixture('default_502', 502), MockRequest.new
110
+ error = Contentful::Management::Error[response.raw.status].new(response)
111
+
112
+ message = "HTTP status code: 502\n"\
113
+ "Message: The requested space is hibernated.\n"\
114
+ "Request ID: 85f-351076632"
115
+ expect(error.message).to eq message
116
+ end
117
+
118
+ it '503' do
119
+ response = Contentful::Management::Response.new raw_fixture('default_503', 503), MockRequest.new
120
+ error = Contentful::Management::Error[response.raw.status].new(response)
121
+
122
+ message = "HTTP status code: 503\n"\
123
+ "Message: Service unavailable.\n"\
124
+ "Request ID: 85f-351076632"
125
+ expect(error.message).to eq message
126
+ end
127
+ end
128
+
129
+ describe 'special cases' do
130
+ describe '400' do
131
+ it 'details is a string' do
132
+ response = Contentful::Management::Response.new raw_fixture('400_details_string', 400), MockRequest.new
133
+ error = Contentful::Management::Error[response.raw.status].new(response)
134
+
135
+ message = "HTTP status code: 400\n"\
136
+ "Message: The request was malformed or missing a required parameter.\n"\
137
+ "Details: some error\n"\
138
+ "Request ID: 85f-351076632"
139
+ expect(error.message).to eq message
140
+ end
141
+
142
+ it 'details is an object, internal errors are strings' do
143
+ response = Contentful::Management::Response.new raw_fixture('400_details_errors_string', 400), MockRequest.new
144
+ error = Contentful::Management::Error[response.raw.status].new(response)
145
+
146
+ message = "HTTP status code: 400\n"\
147
+ "Message: The request was malformed or missing a required parameter.\n"\
148
+ "Details: some error\n"\
149
+ "Request ID: 85f-351076632"
150
+ expect(error.message).to eq message
151
+ end
152
+
153
+ it 'details is an object, internal errors are objects which have details' do
154
+ response = Contentful::Management::Response.new raw_fixture('400_details_errors_object', 400), MockRequest.new
155
+ error = Contentful::Management::Error[response.raw.status].new(response)
156
+
157
+ message = "HTTP status code: 400\n"\
158
+ "Message: The request was malformed or missing a required parameter.\n"\
159
+ "Details: some error\n"\
160
+ "Request ID: 85f-351076632"
161
+ expect(error.message).to eq message
162
+ end
163
+ end
164
+
165
+ describe '403' do
166
+ it 'has an array of reasons' do
167
+ response = Contentful::Management::Response.new raw_fixture('403_reasons', 403), MockRequest.new
168
+ error = Contentful::Management::Error[response.raw.status].new(response)
169
+
170
+ message = "HTTP status code: 403\n"\
171
+ "Message: The specified token does not have access to the requested resource.\n"\
172
+ "Details: \n\tReasons:\n"\
173
+ "\t\tfoo\n"\
174
+ "\t\tbar\n"\
175
+ "Request ID: 85f-351076632"
176
+ expect(error.message).to eq message
177
+ end
178
+ end
179
+
180
+ describe '404' do
181
+ it 'details is a string' do
182
+ response = Contentful::Management::Response.new raw_fixture('404_details_string', 404), MockRequest.new
183
+ error = Contentful::Management::Error[response.raw.status].new(response)
184
+
185
+ message = "HTTP status code: 404\n"\
186
+ "Message: The requested resource or endpoint could not be found.\n"\
187
+ "Details: The resource could not be found\n"\
188
+ "Request ID: 85f-351076632"
189
+ expect(error.message).to eq message
190
+ end
191
+
192
+ it 'has a type' do
193
+ response = Contentful::Management::Response.new raw_fixture('404_type', 404), MockRequest.new
194
+ error = Contentful::Management::Error[response.raw.status].new(response)
195
+
196
+ message = "HTTP status code: 404\n"\
197
+ "Message: The requested resource or endpoint could not be found.\n"\
198
+ "Details: The requested Asset could not be found.\n"\
199
+ "Request ID: 85f-351076632"
200
+ expect(error.message).to eq message
201
+ end
202
+
203
+ it 'can specify the resource id' do
204
+ response = Contentful::Management::Response.new raw_fixture('404_id', 404), MockRequest.new
205
+ error = Contentful::Management::Error[response.raw.status].new(response)
206
+
207
+ message = "HTTP status code: 404\n"\
208
+ "Message: The requested resource or endpoint could not be found.\n"\
209
+ "Details: The requested Asset could not be found. ID: foobar.\n"\
210
+ "Request ID: 85f-351076632"
211
+ expect(error.message).to eq message
212
+ end
213
+ end
214
+
215
+ describe '422' do
216
+ it 'can show formatted details' do
217
+ response = Contentful::Management::Response.new raw_fixture('422_details', 422), MockRequest.new
218
+ error = Contentful::Management::Error[response.raw.status].new(response)
219
+
220
+ message = "HTTP status code: 422\n"\
221
+ "Message: The resource you sent in the body is invalid.\n"\
222
+ "Details: \n"\
223
+ "\t* Name: taken - Path: 'display_code' - Value: 'en-US'\n"\
224
+ "\t* Name: fallback locale creates a loop - Path: 'fallback_code' - Value: 'en-US'\n"\
225
+ "Request ID: 044bb23356babe4f11a3f7f1e77c762a"
226
+ expect(error.message).to eq message
227
+ end
228
+ end
229
+
230
+ describe '429' do
231
+ it 'can show the time until reset' do
232
+ response = Contentful::Management::Response.new raw_fixture('default_429', 429, false, {'x-contentful-ratelimit-reset' => 60}), MockRequest.new
233
+ error = Contentful::Management::Error[response.raw.status].new(response)
234
+
235
+ message = "HTTP status code: 429\n"\
236
+ "Message: Rate limit exceeded. Too many requests.\n"\
237
+ "Request ID: 85f-351076632\n"\
238
+ "Time until reset (seconds): 60"
239
+ expect(error.message).to eq message
240
+ end
241
+ end
242
+ end
243
+
244
+ describe 'generic error' do
245
+ it 'with everything' do
246
+ response = Contentful::Management::Response.new raw_fixture('other_error', 512), MockRequest.new
247
+ error = Contentful::Management::Error[response.raw.status].new(response)
248
+
249
+ message = "HTTP status code: 512\n"\
250
+ "Message: Some error occurred.\n"\
251
+ "Details: some text\n"\
252
+ "Request ID: 85f-351076632"
253
+ expect(error.message).to eq message
254
+ end
255
+
256
+ it 'no details' do
257
+ response = Contentful::Management::Response.new raw_fixture('other_error_no_details', 512), MockRequest.new
258
+ error = Contentful::Management::Error[response.raw.status].new(response)
259
+
260
+ message = "HTTP status code: 512\n"\
261
+ "Message: Some error occurred.\n"\
262
+ "Request ID: 85f-351076632"
263
+ expect(error.message).to eq message
264
+ end
265
+
266
+ it 'no request id' do
267
+ response = Contentful::Management::Response.new raw_fixture('other_error_no_request_id', 512), MockRequest.new
268
+ error = Contentful::Management::Error[response.raw.status].new(response)
269
+
270
+ message = "HTTP status code: 512\n"\
271
+ "Message: Some error occurred.\n"\
272
+ "Details: some text"
273
+ expect(error.message).to eq message
274
+ end
275
+
276
+ it 'no message' do
277
+ response = Contentful::Management::Response.new raw_fixture('other_error_no_message', 512), MockRequest.new
278
+ error = Contentful::Management::Error[response.raw.status].new(response)
279
+
280
+ message = "HTTP status code: 512\n"\
281
+ "Message: The following error was received: {\n"\
282
+ " \"sys\": {\n"\
283
+ " \"type\": \"Error\",\n"\
284
+ " \"id\": \"SomeError\"\n"\
285
+ " },\n"\
286
+ " \"details\": \"some text\",\n"\
287
+ " \"requestId\": \"85f-351076632\"\n"\
288
+ "}\n"\
289
+ "\n"\
290
+ "Details: some text\n"\
291
+ "Request ID: 85f-351076632"
292
+ expect(error.message).to eq message
293
+ end
294
+
295
+ it 'nothing' do
296
+ response = Contentful::Management::Response.new raw_fixture('other_error_nothing', 512), MockRequest.new
297
+ error = Contentful::Management::Error[response.raw.status].new(response)
298
+
299
+ message = "HTTP status code: 512\n"\
300
+ "Message: The following error was received: {\n"\
301
+ " \"sys\": {\n"\
302
+ " \"type\": \"Error\",\n"\
303
+ " \"id\": \"SomeError\"\n"\
304
+ " }\n"\
305
+ "}\n"
306
+ expect(error.message).to eq message
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ describe Contentful::Management::UnparsableJson do
313
+ describe '#message' do
314
+ it 'returns the json parser\'s message' do
315
+ uj = Contentful::Management::Response.new raw_fixture('unparsable'), MockRequest.new
316
+ expect(Contentful::Management::UnparsableJson.new(uj).message).to \
317
+ include 'unexpected token'
318
+ end
319
+ end
320
+ end
321
+
322
+ describe '.[]' do
323
+ it 'returns BadRequest error class for 400' do
324
+ expect(Contentful::Management::Error[400]).to eq Contentful::Management::BadRequest
325
+ end
326
+
327
+ it 'returns Unauthorized error class for 401' do
328
+ expect(Contentful::Management::Error[401]).to eq Contentful::Management::Unauthorized
329
+ end
330
+
331
+ it 'returns AccessDenied error class for 403' do
332
+ expect(Contentful::Management::Error[403]).to eq Contentful::Management::AccessDenied
333
+ end
334
+
335
+ it 'returns NotFound error class for 404' do
336
+ expect(Contentful::Management::Error[404]).to eq Contentful::Management::NotFound
337
+ end
338
+
339
+ it 'returns Conflict error class for 409' do
340
+ expect(Contentful::Management::Error[409]).to eq Contentful::Management::Conflict
341
+ end
342
+
343
+ it 'returns UnprocessableEntity error class for 422' do
344
+ expect(Contentful::Management::Error[422]).to eq Contentful::Management::UnprocessableEntity
345
+ end
346
+
347
+ it 'returns RateLimitExceeded error class for 429' do
348
+ expect(Contentful::Management::Error[429]).to eq Contentful::Management::RateLimitExceeded
349
+ end
350
+
351
+ it 'returns ServerError error class for 500' do
352
+ expect(Contentful::Management::Error[500]).to eq Contentful::Management::ServerError
353
+ end
354
+
355
+ it 'returns BadGateway error class for 502' do
356
+ expect(Contentful::Management::Error[502]).to eq Contentful::Management::BadGateway
357
+ end
358
+
359
+ it 'returns ServiceUnavailable error class for 503' do
360
+ expect(Contentful::Management::Error[503]).to eq Contentful::Management::ServiceUnavailable
361
+ end
362
+
363
+ it 'returns generic error class for any other value' do
364
+ expect(Contentful::Management::Error[nil]).to eq Contentful::Management::Error
365
+ expect(Contentful::Management::Error[200]).to eq Contentful::Management::Error
366
+ end
367
+ end
368
+ end
369
+
@@ -0,0 +1,18 @@
1
+ require 'multi_json'
2
+
3
+ def raw_fixture(which, status = 200, _as_json = false, headers = {})
4
+ object = Object.new
5
+ allow(object).to receive(:status) { status }
6
+ allow(object).to receive(:headers) { headers }
7
+ allow(object).to receive(:to_s) { File.read File.dirname(__FILE__) + "/../fixtures/json_responses/#{which}.json" }
8
+ allow(object).to receive(:body) { object.to_s }
9
+ allow(object).to receive(:[]) { |key| object.headers[key] }
10
+
11
+ object
12
+ end
13
+
14
+ def json_fixture(which, _as_json = false)
15
+ MultiJson.load(
16
+ File.read File.dirname(__FILE__) + "/../fixtures/json_responses/#{which}.json"
17
+ )
18
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contentful-management
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Protas
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-10-09 00:00:00.000000000 Z
13
+ date: 2017-10-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: http
@@ -375,6 +375,31 @@ files:
375
375
  - lib/contentful/management/webhook_health.rb
376
376
  - lib/contentful/management/webhook_webhook_call_methods_factory.rb
377
377
  - lib/contentful/management/webhook_webhook_health_methods_factory.rb
378
+ - spec/fixtures/json_responses/400_details_errors_object.json
379
+ - spec/fixtures/json_responses/400_details_errors_string.json
380
+ - spec/fixtures/json_responses/400_details_string.json
381
+ - spec/fixtures/json_responses/403_reasons.json
382
+ - spec/fixtures/json_responses/404_details_string.json
383
+ - spec/fixtures/json_responses/404_id.json
384
+ - spec/fixtures/json_responses/404_type.json
385
+ - spec/fixtures/json_responses/422_details.json
386
+ - spec/fixtures/json_responses/default_400.json
387
+ - spec/fixtures/json_responses/default_401.json
388
+ - spec/fixtures/json_responses/default_403.json
389
+ - spec/fixtures/json_responses/default_404.json
390
+ - spec/fixtures/json_responses/default_409.json
391
+ - spec/fixtures/json_responses/default_422.json
392
+ - spec/fixtures/json_responses/default_429.json
393
+ - spec/fixtures/json_responses/default_500.json
394
+ - spec/fixtures/json_responses/default_502.json
395
+ - spec/fixtures/json_responses/default_503.json
396
+ - spec/fixtures/json_responses/not_found.json
397
+ - spec/fixtures/json_responses/other_error.json
398
+ - spec/fixtures/json_responses/other_error_no_details.json
399
+ - spec/fixtures/json_responses/other_error_no_message.json
400
+ - spec/fixtures/json_responses/other_error_no_request_id.json
401
+ - spec/fixtures/json_responses/other_error_nothing.json
402
+ - spec/fixtures/json_responses/unparsable.json
378
403
  - spec/fixtures/pixel.jpg
379
404
  - spec/fixtures/vcr_cassettes/api_key/all_for_space.yml
380
405
  - spec/fixtures/vcr_cassettes/api_key/create_for_space.yml
@@ -676,6 +701,7 @@ files:
676
701
  - spec/lib/contentful/management/content_type_spec.rb
677
702
  - spec/lib/contentful/management/editor_interface_spec.rb
678
703
  - spec/lib/contentful/management/entry_spec.rb
704
+ - spec/lib/contentful/management/error_class_spec.rb
679
705
  - spec/lib/contentful/management/locale_spec.rb
680
706
  - spec/lib/contentful/management/organization_spec.rb
681
707
  - spec/lib/contentful/management/personal_access_token_spec.rb
@@ -690,6 +716,7 @@ files:
690
716
  - spec/lib/contentful/management/webhook_health_spec.rb
691
717
  - spec/lib/contentful/management/webhook_spec.rb
692
718
  - spec/spec_helper.rb
719
+ - spec/support/json_responses.rb
693
720
  - spec/support/vcr.rb
694
721
  homepage: https://github.com/contentful/contentful-management.rb
695
722
  licenses:
@@ -716,6 +743,31 @@ signing_key:
716
743
  specification_version: 4
717
744
  summary: contentful management api
718
745
  test_files:
746
+ - spec/fixtures/json_responses/400_details_errors_object.json
747
+ - spec/fixtures/json_responses/400_details_errors_string.json
748
+ - spec/fixtures/json_responses/400_details_string.json
749
+ - spec/fixtures/json_responses/403_reasons.json
750
+ - spec/fixtures/json_responses/404_details_string.json
751
+ - spec/fixtures/json_responses/404_id.json
752
+ - spec/fixtures/json_responses/404_type.json
753
+ - spec/fixtures/json_responses/422_details.json
754
+ - spec/fixtures/json_responses/default_400.json
755
+ - spec/fixtures/json_responses/default_401.json
756
+ - spec/fixtures/json_responses/default_403.json
757
+ - spec/fixtures/json_responses/default_404.json
758
+ - spec/fixtures/json_responses/default_409.json
759
+ - spec/fixtures/json_responses/default_422.json
760
+ - spec/fixtures/json_responses/default_429.json
761
+ - spec/fixtures/json_responses/default_500.json
762
+ - spec/fixtures/json_responses/default_502.json
763
+ - spec/fixtures/json_responses/default_503.json
764
+ - spec/fixtures/json_responses/not_found.json
765
+ - spec/fixtures/json_responses/other_error.json
766
+ - spec/fixtures/json_responses/other_error_no_details.json
767
+ - spec/fixtures/json_responses/other_error_no_message.json
768
+ - spec/fixtures/json_responses/other_error_no_request_id.json
769
+ - spec/fixtures/json_responses/other_error_nothing.json
770
+ - spec/fixtures/json_responses/unparsable.json
719
771
  - spec/fixtures/pixel.jpg
720
772
  - spec/fixtures/vcr_cassettes/api_key/all_for_space.yml
721
773
  - spec/fixtures/vcr_cassettes/api_key/create_for_space.yml
@@ -1017,6 +1069,7 @@ test_files:
1017
1069
  - spec/lib/contentful/management/content_type_spec.rb
1018
1070
  - spec/lib/contentful/management/editor_interface_spec.rb
1019
1071
  - spec/lib/contentful/management/entry_spec.rb
1072
+ - spec/lib/contentful/management/error_class_spec.rb
1020
1073
  - spec/lib/contentful/management/locale_spec.rb
1021
1074
  - spec/lib/contentful/management/organization_spec.rb
1022
1075
  - spec/lib/contentful/management/personal_access_token_spec.rb
@@ -1031,4 +1084,5 @@ test_files:
1031
1084
  - spec/lib/contentful/management/webhook_health_spec.rb
1032
1085
  - spec/lib/contentful/management/webhook_spec.rb
1033
1086
  - spec/spec_helper.rb
1087
+ - spec/support/json_responses.rb
1034
1088
  - spec/support/vcr.rb