contentful-management 1.9.0 → 1.10.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 (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