contentful 2.1.3 → 2.2.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 (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')