contentful 2.1.3 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile.lock +24 -26
  4. data/README.md +5 -0
  5. data/coverage/.last_run.json +1 -1
  6. data/coverage/.resultset.json +2399 -460
  7. data/coverage/assets/{0.10.1 → 0.10.2}/application.css +0 -0
  8. data/coverage/assets/{0.10.1 → 0.10.2}/application.js +0 -0
  9. data/coverage/assets/{0.10.1 → 0.10.2}/colorbox/border.png +0 -0
  10. data/coverage/assets/{0.10.1 → 0.10.2}/colorbox/controls.png +0 -0
  11. data/coverage/assets/{0.10.1 → 0.10.2}/colorbox/loading.gif +0 -0
  12. data/coverage/assets/{0.10.1 → 0.10.2}/colorbox/loading_background.png +0 -0
  13. data/coverage/assets/{0.10.1 → 0.10.2}/favicon_green.png +0 -0
  14. data/coverage/assets/{0.10.1 → 0.10.2}/favicon_red.png +0 -0
  15. data/coverage/assets/{0.10.1 → 0.10.2}/favicon_yellow.png +0 -0
  16. data/coverage/assets/{0.10.1 → 0.10.2}/loading.gif +0 -0
  17. data/coverage/assets/{0.10.1 → 0.10.2}/magnify.png +0 -0
  18. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  19. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  20. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  21. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  22. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  23. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  24. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  25. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  26. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-icons_222222_256x240.png +0 -0
  27. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  28. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-icons_454545_256x240.png +0 -0
  29. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-icons_888888_256x240.png +0 -0
  30. data/coverage/assets/{0.10.1 → 0.10.2}/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  31. data/coverage/index.html +17490 -3213
  32. data/doc/Contentful.html +3 -3
  33. data/doc/Contentful/AccessDenied.html +1 -1
  34. data/doc/Contentful/Array.html +1 -1
  35. data/doc/Contentful/ArrayCoercion.html +7 -7
  36. data/doc/Contentful/ArrayLike.html +1 -1
  37. data/doc/Contentful/Asset.html +1 -1
  38. data/doc/Contentful/BadGateway.html +159 -0
  39. data/doc/Contentful/BadRequest.html +1 -1
  40. data/doc/Contentful/BaseCoercion.html +1 -1
  41. data/doc/Contentful/BaseResource.html +2 -2
  42. data/doc/Contentful/BooleanCoercion.html +1 -1
  43. data/doc/Contentful/Client.html +20 -26
  44. data/doc/Contentful/ContentType.html +7 -1
  45. data/doc/Contentful/ContentTypeCache.html +1 -1
  46. data/doc/Contentful/DateCoercion.html +1 -1
  47. data/doc/Contentful/DeletedAsset.html +1 -1
  48. data/doc/Contentful/DeletedEntry.html +1 -1
  49. data/doc/Contentful/Entry.html +1 -1
  50. data/doc/Contentful/Error.html +16 -28
  51. data/doc/Contentful/Field.html +1 -1
  52. data/doc/Contentful/FieldsResource.html +1 -1
  53. data/doc/Contentful/File.html +8 -308
  54. data/doc/Contentful/FloatCoercion.html +1 -1
  55. data/doc/Contentful/IntegerCoercion.html +1 -1
  56. data/doc/Contentful/Link.html +1 -1
  57. data/doc/Contentful/LinkCoercion.html +1 -1
  58. data/doc/Contentful/Locale.html +1 -1
  59. data/doc/Contentful/Location.html +1 -1
  60. data/doc/Contentful/LocationCoercion.html +1 -1
  61. data/doc/Contentful/NotFound.html +1 -1
  62. data/doc/Contentful/ObjectCoercion.html +2 -2
  63. data/doc/Contentful/RateLimitExceeded.html +183 -2
  64. data/doc/Contentful/Request.html +1 -1
  65. data/doc/Contentful/ResourceBuilder.html +1 -1
  66. data/doc/Contentful/Response.html +72 -1
  67. data/doc/Contentful/ServerError.html +1 -1
  68. data/doc/Contentful/ServiceUnavailable.html +1 -1
  69. data/doc/Contentful/Space.html +1 -1
  70. data/doc/Contentful/StringCoercion.html +1 -1
  71. data/doc/Contentful/Support.html +49 -27
  72. data/doc/Contentful/SymbolCoercion.html +1 -1
  73. data/doc/Contentful/Sync.html +1 -1
  74. data/doc/Contentful/SyncPage.html +1 -1
  75. data/doc/Contentful/TextCoercion.html +1 -1
  76. data/doc/Contentful/Unauthorized.html +1 -1
  77. data/doc/Contentful/UnparsableJson.html +1 -1
  78. data/doc/Contentful/UnparsableResource.html +1 -1
  79. data/doc/_index.html +8 -1
  80. data/doc/class_list.html +1 -1
  81. data/doc/file.CHANGELOG.html +15 -1
  82. data/doc/file.LICENSE.html +1 -1
  83. data/doc/file.README.html +8 -1
  84. data/doc/index.html +8 -1
  85. data/doc/method_list.html +147 -155
  86. data/doc/top-level-namespace.html +1 -1
  87. data/lib/contentful/asset.rb +2 -2
  88. data/lib/contentful/base_resource.rb +3 -3
  89. data/lib/contentful/client.rb +3 -3
  90. data/lib/contentful/coercions.rb +1 -20
  91. data/lib/contentful/content_type.rb +2 -0
  92. data/lib/contentful/entry.rb +4 -2
  93. data/lib/contentful/error.rb +168 -29
  94. data/lib/contentful/fields_resource.rb +8 -5
  95. data/lib/contentful/file.rb +14 -6
  96. data/lib/contentful/response.rb +5 -4
  97. data/lib/contentful/support.rb +4 -1
  98. data/lib/contentful/version.rb +1 -1
  99. data/spec/asset_spec.rb +9 -0
  100. data/spec/content_type_spec.rb +10 -0
  101. data/spec/deleted_asset_spec.rb +10 -0
  102. data/spec/deleted_entry_spec.rb +10 -0
  103. data/spec/entry_spec.rb +11 -0
  104. data/spec/error_class_spec.rb +244 -3
  105. data/spec/field_spec.rb +21 -0
  106. data/spec/file_spec.rb +10 -0
  107. data/spec/fixtures/json_responses/400_details_errors_object.json +14 -0
  108. data/spec/fixtures/json_responses/400_details_errors_string.json +12 -0
  109. data/spec/fixtures/json_responses/400_details_string.json +8 -0
  110. data/spec/fixtures/json_responses/403_reasons.json +13 -0
  111. data/spec/fixtures/json_responses/404_id.json +11 -0
  112. data/spec/fixtures/json_responses/404_type.json +10 -0
  113. data/spec/fixtures/json_responses/default_400.json +7 -0
  114. data/spec/fixtures/json_responses/default_401.json +7 -0
  115. data/spec/fixtures/json_responses/default_403.json +7 -0
  116. data/spec/fixtures/json_responses/default_404.json +7 -0
  117. data/spec/fixtures/json_responses/default_429.json +7 -0
  118. data/spec/fixtures/json_responses/default_500.json +7 -0
  119. data/spec/fixtures/json_responses/default_502.json +7 -0
  120. data/spec/fixtures/json_responses/default_503.json +7 -0
  121. data/spec/fixtures/json_responses/other_error.json +9 -0
  122. data/spec/fixtures/json_responses/other_error_no_details.json +8 -0
  123. data/spec/fixtures/json_responses/other_error_no_message.json +8 -0
  124. data/spec/fixtures/json_responses/other_error_no_request_id.json +8 -0
  125. data/spec/fixtures/json_responses/other_error_nothing.json +6 -0
  126. data/spec/link_spec.rb +9 -0
  127. data/spec/support/json_responses.rb +4 -2
  128. metadata +66 -27
@@ -100,7 +100,7 @@
100
100
  </div>
101
101
 
102
102
  <div id="footer">
103
- Generated on Thu Aug 17 09:42:18 2017 by
103
+ Generated on Tue Oct 24 14:05:58 2017 by
104
104
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
105
  0.9.9 (ruby-2.4.1).
106
106
  </div>
@@ -73,10 +73,10 @@ module Contentful
73
73
  is_localized = file_json.keys.none? { |f| %w(fileName contentType details url).include? f }
74
74
  if is_localized
75
75
  locales.each do |locale|
76
- @fields[locale][:file] = ::Contentful::File.new(file_json[locale.to_s] || {})
76
+ @fields[locale][:file] = ::Contentful::File.new(file_json[locale.to_s] || {}, @configuration)
77
77
  end
78
78
  else
79
- @fields[internal_resource_locale][:file] = ::Contentful::File.new(file_json)
79
+ @fields[internal_resource_locale][:file] = ::Contentful::File.new(file_json, @configuration)
80
80
  end
81
81
  end
82
82
 
@@ -9,8 +9,8 @@ module Contentful
9
9
  @raw = item
10
10
  @default_locale = configuration[:default_locale]
11
11
  @depth = depth
12
- @sys = hydrate_sys
13
12
  @configuration = configuration
13
+ @sys = hydrate_sys
14
14
 
15
15
  define_sys_methods!
16
16
  end
@@ -69,7 +69,7 @@ module Contentful
69
69
  elsif %w(createdAt updatedAt deletedAt).include?(k)
70
70
  v = DateTime.parse(v)
71
71
  end
72
- result[Support.snakify(k).to_sym] = v
72
+ result[Support.snakify(k, @configuration[:use_camel_case]).to_sym] = v
73
73
  end
74
74
  result
75
75
  end
@@ -86,7 +86,7 @@ module Contentful
86
86
 
87
87
  def build_link(item)
88
88
  require_relative 'link'
89
- ::Contentful::Link.new(item)
89
+ ::Contentful::Link.new(item, @configuration)
90
90
  end
91
91
  end
92
92
  end
@@ -36,13 +36,12 @@ module Contentful
36
36
  max_rate_limit_retries: 1,
37
37
  max_rate_limit_wait: 60,
38
38
  max_include_resolution_depth: 20,
39
+ use_camel_case: false,
39
40
  application_name: nil,
40
41
  application_version: nil,
41
42
  integration_name: nil,
42
43
  integration_version: nil
43
44
  }
44
- # Rate Limit Reset Header Key
45
- RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'
46
45
 
47
46
  attr_reader :configuration, :logger, :proxy
48
47
 
@@ -70,6 +69,7 @@ module Contentful
70
69
  # @option given_configuration [Number] :max_rate_limit_retries
71
70
  # @option given_configuration [Number] :max_rate_limit_wait
72
71
  # @option given_configuration [Number] :max_include_resolution_depth
72
+ # @option given_configuration [Boolean] :use_camel_case
73
73
  # @option given_configuration [Boolean] :gzip_encoded
74
74
  # @option given_configuration [Boolean] :raw_mode
75
75
  # @option given_configuration [false, ::Logger] :logger
@@ -294,7 +294,7 @@ module Contentful
294
294
  raise error if configuration[:raise_errors]
295
295
  return error
296
296
  rescue Contentful::RateLimitExceeded => rate_limit_error
297
- reset_time = rate_limit_error.response.raw[RATE_LIMIT_RESET_HEADER_KEY].to_i
297
+ reset_time = rate_limit_error.reset_time.to_i
298
298
  if should_retry(retries_left, reset_time, configuration[:max_rate_limit_wait])
299
299
  retries_left -= 1
300
300
  logger.info(retry_message(retries_left, reset_time)) if logger
@@ -75,26 +75,7 @@ module Contentful
75
75
  class ObjectCoercion < BaseCoercion
76
76
  # Coerces value to hash, symbolizing each key
77
77
  def coerce
78
- symbolize_recursive(value)
79
- end
80
-
81
- private
82
-
83
- def symbolize_recursive(hash)
84
- {}.tap do |h|
85
- hash.each { |key, value| h[key.to_sym] = map_value(value) }
86
- end
87
- end
88
-
89
- def map_value(thing)
90
- case thing
91
- when Hash
92
- symbolize_recursive(thing)
93
- when Array
94
- thing.map { |v| map_value(v) }
95
- else
96
- thing
97
- end
78
+ JSON.parse(JSON.dump(value), symbolize_names: true)
98
79
  end
99
80
  end
100
81
 
@@ -22,6 +22,8 @@ module Contentful
22
22
  fields.detect { |f| Support.snakify(f.id) == Support.snakify(field_id) }
23
23
  end
24
24
 
25
+ alias displayField display_field
26
+
25
27
  protected
26
28
 
27
29
  def repr_name
@@ -16,7 +16,8 @@ module Contentful
16
16
  return build_nested_resource(value, includes) if Support.link?(value)
17
17
  return coerce_link_array(value, includes) if Support.link_array?(value)
18
18
 
19
- content_type = ContentTypeCache.cache_get(sys[:space].id, sys[:content_type].id)
19
+ content_type_key = Support.snakify('contentType', @configuration[:use_camel_case])
20
+ content_type = ContentTypeCache.cache_get(sys[:space].id, sys[content_type_key.to_sym].id)
20
21
 
21
22
  unless content_type.nil?
22
23
  content_type_field = content_type.field_for(field_id)
@@ -76,7 +77,8 @@ module Contentful
76
77
  protected
77
78
 
78
79
  def repr_name
79
- "#{super}[#{sys[:content_type].id}]"
80
+ content_type_key = Support.snakify('contentType', @configuration[:use_camel_case]).to_sym
81
+ "#{super}[#{sys[content_type_key].id}]"
80
82
  end
81
83
  end
82
84
  end
@@ -6,56 +6,195 @@ module Contentful
6
6
 
7
7
  def initialize(response)
8
8
  @response = response
9
- super @response.error_message
9
+ super best_available_message
10
10
  end
11
11
 
12
12
  # Shortcut for creating specialized error classes
13
13
  # USAGE rescue Contentful::Error[404]
14
14
  def self.[](error_status_code)
15
- case error_status_code
16
- when 404
17
- NotFound
18
- when 400
19
- BadRequest
20
- when 403
21
- AccessDenied
22
- when 401
23
- Unauthorized
24
- when 429
25
- RateLimitExceeded
26
- when 500
27
- ServerError
28
- when 503
29
- ServiceUnavailable
30
- else
31
- Error
15
+ errors = {
16
+ 400 => BadRequest,
17
+ 401 => Unauthorized,
18
+ 403 => AccessDenied,
19
+ 404 => NotFound,
20
+ 429 => RateLimitExceeded,
21
+ 500 => ServerError,
22
+ 502 => BadGateway,
23
+ 503 => ServiceUnavailable
24
+ }
25
+
26
+ errors.key?(error_status_code) ? errors[error_status_code] : Error
27
+ end
28
+
29
+ protected
30
+
31
+ def default_error_message
32
+ "The following error was received: #{@response.raw.body}"
33
+ end
34
+
35
+ def handle_details(details)
36
+ details.to_s
37
+ end
38
+
39
+ def additional_info?
40
+ false
41
+ end
42
+
43
+ def additional_info
44
+ []
45
+ end
46
+
47
+ def best_available_message
48
+ error_message = [
49
+ "HTTP status code: #{@response.raw.status}"
50
+ ]
51
+
52
+ begin
53
+ response_json = @response.load_json
54
+ message = response_json.fetch('message', default_error_message)
55
+ details = response_json.fetch('details', nil)
56
+ request_id = response_json.fetch('requestId', nil)
57
+
58
+ error_message << "Message: #{message}"
59
+ error_message << "Details: #{handle_details(details)}" if details
60
+ error_message << "Request ID: #{request_id}" if request_id
61
+ rescue
62
+ error_message << "Message: #{default_error_message}"
32
63
  end
64
+
65
+ error_message << additional_info if additional_info?
66
+
67
+ error_message.join("\n")
33
68
  end
34
69
  end
35
70
 
36
- # 404
37
- class NotFound < Error; end
38
-
39
71
  # 400
40
- class BadRequest < Error; end
72
+ class BadRequest < Error
73
+ protected
41
74
 
42
- # 403
43
- class AccessDenied < Error; end
75
+ def default_error_message
76
+ 'The request was malformed or missing a required parameter.'
77
+ end
78
+
79
+ def handle_details(details)
80
+ return details if details.is_a?(String)
81
+
82
+ handle_detail = proc do |detail|
83
+ return detail if detail.is_a?(String)
84
+ detail.fetch('details', nil)
85
+ end
86
+
87
+ inner_details = details['errors'].map { |detail| handle_detail[detail] }.reject(&:nil?)
88
+ inner_details.join("\n\t")
89
+ end
90
+ end
44
91
 
45
92
  # 401
46
- class Unauthorized < Error; end
93
+ class Unauthorized < Error
94
+ protected
95
+
96
+ def default_error_message
97
+ 'The authorization token was invalid.'
98
+ end
99
+ end
100
+
101
+ # 403
102
+ class AccessDenied < Error
103
+ protected
104
+
105
+ def default_error_message
106
+ 'The specified token does not have access to the requested resource.'
107
+ end
108
+
109
+ def handle_details(details)
110
+ "\n\tReasons:\n\t\t#{details['reasons'].join("\n\t\t")}"
111
+ end
112
+ end
113
+
114
+ # 404
115
+ class NotFound < Error
116
+ protected
117
+
118
+ def default_error_message
119
+ 'The requested resource or endpoint could not be found.'
120
+ end
121
+
122
+ def handle_details(details)
123
+ message = "The requested #{details['type']} could not be found."
124
+
125
+ resource_id = details.fetch('id', nil)
126
+ message += " ID: #{resource_id}." if resource_id
127
+
128
+ message
129
+ end
130
+ end
47
131
 
48
132
  # 429
49
- class RateLimitExceeded < Error; end
133
+ class RateLimitExceeded < Error
134
+ # Rate Limit Reset Header Key
135
+ RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'
136
+
137
+ def reset_time?
138
+ # rubocop:disable Style/DoubleNegation
139
+ !!reset_time
140
+ # rubocop:enable Style/DoubleNegation
141
+ end
142
+
143
+ # Time until next available request, in seconds.
144
+ def reset_time
145
+ @reset_time ||= @response.raw[RATE_LIMIT_RESET_HEADER_KEY]
146
+ end
147
+
148
+ protected
149
+
150
+ def additional_info?
151
+ reset_time?
152
+ end
153
+
154
+ def additional_info
155
+ ["Time until reset (seconds): #{reset_time}"]
156
+ end
157
+
158
+ def default_error_message
159
+ 'Rate limit exceeded. Too many requests.'
160
+ end
161
+ end
50
162
 
51
163
  # 500
52
- class ServerError < Error; end
164
+ class ServerError < Error
165
+ protected
166
+
167
+ def default_error_message
168
+ 'Internal server error.'
169
+ end
170
+ end
171
+
172
+ # 502
173
+ class BadGateway < Error
174
+ protected
175
+
176
+ def default_error_message
177
+ 'The requested space is hibernated.'
178
+ end
179
+ end
53
180
 
54
181
  # 503
55
- class ServiceUnavailable < Error; end
182
+ class ServiceUnavailable < Error
183
+ protected
184
+
185
+ def default_error_message
186
+ 'The request was malformed or missing a required parameter.'
187
+ end
188
+ end
56
189
 
57
190
  # Raised when response is no valid json
58
- class UnparsableJson < Error; end
191
+ class UnparsableJson < Error
192
+ protected
193
+
194
+ def default_error_message
195
+ @response.error_message
196
+ end
197
+ end
59
198
 
60
199
  # Raised when response is not parsable as a Contentful::Resource
61
200
  class UnparsableResource < StandardError; end
@@ -65,7 +65,8 @@ module Contentful
65
65
  links = fields.keys.select { |property| known_link?(property) }
66
66
  processed_raw = raw.clone
67
67
  raw['fields'].each do |k, v|
68
- processed_raw['fields'][k] = links.include?(Support.snakify(k).to_sym) ? send(Support.snakify(k)) : v
68
+ links_key = Support.snakify(k, @configuration[:use_camel_case])
69
+ processed_raw['fields'][k] = links.include?(links_key.to_sym) ? send(links_key) : v
69
70
  end
70
71
 
71
72
  processed_raw
@@ -91,8 +92,9 @@ module Contentful
91
92
  raw['fields'].each do |name, locales|
92
93
  locales.each do |loc, value|
93
94
  result[loc] ||= {}
94
- result[loc][Support.snakify(name).to_sym] = coerce(
95
- Support.snakify(name),
95
+ name = Support.snakify(name, @configuration[:use_camel_case])
96
+ result[loc][name.to_sym] = coerce(
97
+ name,
96
98
  value,
97
99
  includes
98
100
  )
@@ -100,8 +102,9 @@ module Contentful
100
102
  end
101
103
  else
102
104
  raw['fields'].each do |name, value|
103
- result[locale][Support.snakify(name).to_sym] = coerce(
104
- Support.snakify(name),
105
+ name = Support.snakify(name, @configuration[:use_camel_case])
106
+ result[locale][name.to_sym] = coerce(
107
+ name,
105
108
  value,
106
109
  includes
107
110
  )
@@ -1,12 +1,20 @@
1
1
  module Contentful
2
2
  # An Assets's file info
3
3
  class File
4
- attr_reader :file_name, :content_type, :details, :url
5
- def initialize(json)
6
- @file_name = json.fetch('fileName', nil)
7
- @content_type = json.fetch('contentType', nil)
8
- @details = json.fetch('details', nil)
9
- @url = json.fetch('url', nil)
4
+ def initialize(json, configuration)
5
+ @configuration = configuration
6
+
7
+ define_fields!(json)
8
+ end
9
+
10
+ private
11
+
12
+ def define_fields!(json)
13
+ json.each do |k, v|
14
+ define_singleton_method Support.snakify(k, @configuration[:use_camel_case]) do
15
+ v
16
+ end
17
+ end
10
18
  end
11
19
  end
12
20
  end
@@ -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?
@@ -88,10 +93,6 @@ module Contentful
88
93
  UnparsableJson.new(self)
89
94
  end
90
95
 
91
- def load_json
92
- MultiJson.load(unzip_response(raw))
93
- end
94
-
95
96
  def unzip_response(response)
96
97
  parsed_response = response.to_s
97
98
  if response.headers['Content-Encoding'].eql?('gzip')