contentful 2.14.0 → 2.15.4

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.
@@ -11,13 +11,15 @@ module Contentful
11
11
 
12
12
  include Contentful::ArrayLike
13
13
 
14
- attr_reader :total, :limit, :skip, :items, :endpoint
14
+ attr_reader :total, :limit, :skip, :items, :endpoint, :query
15
15
 
16
16
  def initialize(item = nil,
17
17
  configuration = {
18
18
  default_locale: Contentful::Client::DEFAULT_CONFIGURATION[:default_locale]
19
19
  },
20
- endpoint = '', *)
20
+ endpoint = '',
21
+ query = {},
22
+ *)
21
23
  super(item, configuration)
22
24
 
23
25
  @endpoint = endpoint
@@ -25,11 +27,12 @@ module Contentful
25
27
  @limit = item.fetch('limit', nil)
26
28
  @skip = item.fetch('skip', nil)
27
29
  @items = item.fetch('items', [])
30
+ @query = query
28
31
  end
29
32
 
30
33
  # @private
31
34
  def marshal_dump
32
- super.merge(endpoint: endpoint)
35
+ super.merge(endpoint: endpoint, query: query)
33
36
  end
34
37
 
35
38
  # @private
@@ -39,6 +42,7 @@ module Contentful
39
42
  @total = raw.fetch('total', nil)
40
43
  @limit = raw.fetch('limit', nil)
41
44
  @skip = raw.fetch('skip', nil)
45
+ @query = raw_object[:query]
42
46
  @items = raw.fetch('items', []).map do |item|
43
47
  require_relative 'resource_builder'
44
48
  ResourceBuilder.new(
@@ -72,7 +76,8 @@ module Contentful
72
76
  'Asset' => 'assets',
73
77
  'Locale' => 'locales'
74
78
  }
75
- client.public_send(plurals[items.first.type], limit: limit, skip: new_skip)
79
+
80
+ client.public_send(plurals[items.first.type], query.merge(limit: limit, skip: new_skip))
76
81
  end
77
82
  end
78
83
  end
@@ -37,8 +37,8 @@ module Contentful
37
37
  # Delegates to items#[]
38
38
  #
39
39
  # @return [Contentful::Entry, Contentful::Asset]
40
- def [](index)
41
- items[index]
40
+ def [](*args)
41
+ items[*args]
42
42
  end
43
43
 
44
44
  # Delegates to items#last
@@ -10,10 +10,7 @@ module Contentful
10
10
 
11
11
  # @private
12
12
  def marshal_dump
13
- {
14
- configuration: @configuration,
15
- raw: raw
16
- }
13
+ super.merge(raw: raw)
17
14
  end
18
15
 
19
16
  # @private
@@ -23,6 +20,11 @@ module Contentful
23
20
  define_asset_methods!
24
21
  end
25
22
 
23
+ # @private
24
+ def known_link?(*)
25
+ false
26
+ end
27
+
26
28
  # @private
27
29
  def inspect
28
30
  "<#{repr_name} id='#{sys[:id]}' url='#{url}'>"
@@ -91,6 +93,10 @@ module Contentful
91
93
  end
92
94
 
93
95
  def define_asset_methods!
96
+ define_singleton_method :title do
97
+ fields.fetch(:title, nil)
98
+ end
99
+
94
100
  define_singleton_method :description do
95
101
  fields.fetch(:description, nil)
96
102
  end
@@ -31,14 +31,28 @@ module Contentful
31
31
 
32
32
  # @private
33
33
  def marshal_dump
34
+ entry_mapping = @configuration[:entry_mapping].each_with_object({}) do |(k, v), res|
35
+ res[k] = v.to_s
36
+ end
37
+
34
38
  {
35
- configuration: @configuration,
39
+ # loggers usually have a file handle that can't be marshalled, so let's not return that
40
+ configuration: @configuration.merge(entry_mapping: entry_mapping, logger: nil),
36
41
  raw: raw
37
42
  }
38
43
  end
39
44
 
40
45
  # @private
41
46
  def marshal_load(raw_object)
47
+ raw_object[:configuration][:entry_mapping] = raw_object[:configuration].fetch(:entry_mapping, {}).each_with_object({}) do |(k, v), res|
48
+ begin
49
+ v = v.to_s unless v.is_a?(::String)
50
+ res[k] = v.split('::').inject(Object) { |o, c| o.const_get c }
51
+ rescue
52
+ next
53
+ end
54
+ end
55
+
42
56
  @raw = raw_object[:raw]
43
57
  @configuration = raw_object[:configuration]
44
58
  @default_locale = @configuration[:default_locale]
@@ -30,7 +30,6 @@ module Contentful
30
30
  raw_mode: false,
31
31
  gzip_encoded: true,
32
32
  logger: false,
33
- log_level: Logger::INFO,
34
33
  proxy_host: nil,
35
34
  proxy_username: nil,
36
35
  proxy_password: nil,
@@ -105,7 +104,7 @@ module Contentful
105
104
  # @private
106
105
  def setup_logger
107
106
  @logger = configuration[:logger]
108
- logger.level = configuration[:log_level] if logger
107
+ logger.level = configuration[:log_level] if logger && configuration.key?(:log_level)
109
108
  end
110
109
 
111
110
  # @private
@@ -386,7 +385,9 @@ module Contentful
386
385
  response.object,
387
386
  configuration.merge(endpoint: response.request.endpoint),
388
387
  (response.request.query || {}).fetch(:locale, nil) == '*',
389
- 0
388
+ 0,
389
+ [],
390
+ response.request.query || {}
390
391
  ).run
391
392
  end
392
393
 
@@ -17,7 +17,8 @@ module Contentful
17
17
  private
18
18
 
19
19
  def coerce(field_id, value, includes, errors, entries = {})
20
- if Support.link?(value) && !Support.unresolvable?(value, errors)
20
+ if Support.link?(value)
21
+ return nil if Support.unresolvable?(value, errors)
21
22
  return build_nested_resource(value, includes, entries, errors)
22
23
  end
23
24
  return coerce_link_array(value, includes, errors, entries) if Support.link_array?(value)
@@ -48,11 +48,7 @@ module Contentful
48
48
 
49
49
  # @private
50
50
  def marshal_dump
51
- {
52
- configuration: @configuration,
53
- raw: raw_with_links,
54
- localized: localized
55
- }
51
+ super.merge(raw: raw_with_links, localized: localized)
56
52
  end
57
53
 
58
54
  # @private
@@ -13,7 +13,12 @@ module Contentful
13
13
 
14
14
  if id
15
15
  @type = :single
16
- @id = URI.escape(id)
16
+ # Given the deprecation of `URI::escape` and `URI::encode`
17
+ # it is needed to replace it with `URI::encode_www_form_component`.
18
+ # This method, does replace spaces with `+` instead of `%20`.
19
+ # Therefore, to keep backwards compatibility, we're replacing the resulting `+`
20
+ # back with `%20`.
21
+ @id = URI.encode_www_form_component(id).gsub('+', '%20')
17
22
  else
18
23
  @type = :multi
19
24
  @id = nil
@@ -32,9 +32,9 @@ module Contentful
32
32
  # Buildable Resources
33
33
  BUILDABLES = %w[Entry Asset ContentType Space DeletedEntry DeletedAsset Locale].freeze
34
34
 
35
- attr_reader :json, :default_locale, :endpoint, :depth, :localized, :resource_mapping, :entry_mapping, :resource
35
+ attr_reader :json, :default_locale, :endpoint, :depth, :localized, :resource_mapping, :entry_mapping, :resource, :query
36
36
 
37
- def initialize(json, configuration = {}, localized = false, depth = 0, errors = [])
37
+ def initialize(json, configuration = {}, localized = false, depth = 0, errors = [], query = {})
38
38
  @json = json
39
39
  @default_locale = configuration.fetch(:default_locale, ::Contentful::Client::DEFAULT_CONFIGURATION[:default_locale])
40
40
  @resource_mapping = default_resource_mapping.merge(configuration.fetch(:resource_mapping, {}))
@@ -46,6 +46,7 @@ module Contentful
46
46
  @configuration = configuration
47
47
  @resource_cache = configuration[:_entries_cache] || {}
48
48
  @errors = errors
49
+ @query = query
49
50
  end
50
51
 
51
52
  # Starts the parsing process.
@@ -69,7 +70,7 @@ module Contentful
69
70
  build_item(item, includes, errors)
70
71
  end
71
72
  array_class = fetch_array_class
72
- array_class.new(json.merge('items' => result), @configuration, endpoint)
73
+ array_class.new(json.merge('items' => result), @configuration, endpoint, query)
73
74
  end
74
75
 
75
76
  def build_single
@@ -1,5 +1,5 @@
1
1
  # Contentful Namespace
2
2
  module Contentful
3
3
  # Gem Version
4
- VERSION = '2.14.0'
4
+ VERSION = '2.15.4'
5
5
  end
@@ -36,6 +36,16 @@ describe Contentful::Array do
36
36
  end
37
37
  end
38
38
 
39
+ describe '#[]' do
40
+ it 'provides access to items by index' do
41
+ expect(array[0]).to be_a Contentful::BaseResource
42
+ end
43
+
44
+ it 'provides access to items by two indices' do
45
+ expect(array[0, 4]).to eq array.items
46
+ end
47
+ end
48
+
39
49
  describe '#each' do
40
50
  it 'is an Enumerator' do
41
51
  expect(array.each).to be_a Enumerator
@@ -63,6 +73,25 @@ describe Contentful::Array do
63
73
  it 'will return false if #request not available' do
64
74
  expect(Contentful::Array.new({}).reload).to be_falsey
65
75
  end
76
+
77
+ it 'respects query parameters' do
78
+ array_page_1 = vcr('query_array_1') { client.entries(content_type: 'cat', limit: 1) }
79
+ array_page_2 = vcr('query_array_2') { array_page_1.next_page(client) }
80
+
81
+ expect(array_page_1).to be_a Contentful::Array
82
+ expect(array_page_2).to be_a Contentful::Array
83
+
84
+ expect(array_page_1.query).to include(content_type: 'cat')
85
+ expect(array_page_2.query).to include(content_type: 'cat')
86
+
87
+ expect(array_page_1.size).to eq 1
88
+ expect(array_page_2.size).to eq 1
89
+
90
+ expect(array_page_1[0].content_type.id).to eq 'cat'
91
+ expect(array_page_2[0].content_type.id).to eq 'cat'
92
+
93
+ expect(array_page_1[0].id).not_to eq array_page_2[0].id
94
+ end
66
95
  end
67
96
 
68
97
  describe 'marshalling' do
@@ -28,7 +28,7 @@ describe Contentful::Client do
28
28
  end
29
29
 
30
30
  it 'uses Request#query' do
31
- expect(request).to receive(:query).twice.and_call_original
31
+ expect(request).to receive(:query).thrice.and_call_original
32
32
  vcr('content_type') { client.get(request) }
33
33
  end
34
34
 
@@ -48,6 +48,23 @@ describe 'Client Configuration Options' do
48
48
  end
49
49
  end
50
50
 
51
+ describe ':log_level' do
52
+
53
+ let(:logger) { Logger.new(STDOUT) }
54
+
55
+ it 'changes the level of the logger instance when given' do
56
+ expect do
57
+ create_client(logger: logger, log_level: ::Logger::WARN)
58
+ end.to change { logger.level }.from(::Logger::DEBUG).to(::Logger::WARN)
59
+ end
60
+
61
+ it 'does not change the level of the logger instance when not given' do
62
+ expect do
63
+ create_client(logger: logger)
64
+ end.not_to change { logger.level }
65
+ end
66
+ end
67
+
51
68
  describe ':dynamic_entries' do
52
69
  before :each do
53
70
  Contentful::ContentTypeCache.clear!
@@ -160,6 +160,7 @@ describe Contentful::Entry do
160
160
  describe 'can be marshalled' do
161
161
  def test_dump(nyancat)
162
162
  dump = Marshal.dump(nyancat)
163
+ yield if block_given?
163
164
  new_cat = Marshal.load(dump)
164
165
 
165
166
  # Attributes
@@ -184,6 +185,14 @@ describe Contentful::Entry do
184
185
  expect(new_cat.image.file.url).to eq "//images.contentful.com/cfexampleapi/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png"
185
186
  end
186
187
 
188
+ it 'marshals properly when entry_mapping changed' do
189
+ vcr('entry/marshall') {
190
+ class TestEntryMapping; end
191
+ nyancat = create_client(gzip_encoded: false, max_include_resolution_depth: 2, entry_mapping: { 'irrelevant_model' => TestEntryMapping }).entries(include: 2, 'sys.id' => 'nyancat').first
192
+ test_dump(nyancat) { Object.send(:remove_const, :TestEntryMapping) }
193
+ }
194
+ end
195
+
187
196
  it 'marshals properly' do
188
197
  vcr('entry/marshall') {
189
198
  nyancat = create_client(gzip_encoded: false, max_include_resolution_depth: 2).entries(include: 2, 'sys.id' => 'nyancat').first
@@ -191,6 +200,16 @@ describe Contentful::Entry do
191
200
  }
192
201
  end
193
202
 
203
+ it 'marshals with a logger set but keeps the instance' do
204
+ logger = Logger.new(IO::NULL)
205
+ vcr('entry/marshall') {
206
+ nyancat = create_client(gzip_encoded: false, max_include_resolution_depth: 2, logger: logger).entries(include: 2, 'sys.id' => 'nyancat').first
207
+ expect(nyancat.instance_variable_get(:@configuration)[:logger]).to eq(logger)
208
+ test_dump(nyancat)
209
+ expect(nyancat.instance_variable_get(:@configuration)[:logger]).to eq(logger)
210
+ }
211
+ end
212
+
194
213
  it 'can remarshall an unmarshalled object' do
195
214
  vcr('entry/marshall') {
196
215
  nyancat = create_client(max_include_resolution_depth: 2).entries(include: 2, 'sys.id' => 'nyancat').first
@@ -477,6 +496,17 @@ describe Contentful::Entry do
477
496
  end
478
497
 
479
498
  describe 'issues' do
499
+ it 'filters out unresolvable assets' do
500
+ vcr('entries/unresolvable_assets') {
501
+ client = create_client(space: 'facgnwwgj5fe', access_token: '4d0f55d940975f78139daae5d965b463c0816e88ad16062d2c1ee3d6cb930521')
502
+ entry = client.entry('gLVzQlb09IeOeU181fwtz')
503
+
504
+ expect(entry.single_asset).to be_nil
505
+ expect(entry.multi_asset.size).to eq 1
506
+ expect(entry.raw["fields"]["multiAsset"].size).to eq 2
507
+ }
508
+ end
509
+
480
510
  it 'Symbol/Text field with null values should be serialized as nil - #117' do
481
511
  vcr('entries/issue_117') {
482
512
  client = create_client(space: '8jbbayggj9gj', access_token: '4ce0108f04e55c76476ba84ab0e6149734db73d67cd1b429323ef67f00977e07')
@@ -0,0 +1,89 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://cdn.contentful.com/spaces/facgnwwgj5fe/environments/master/entries?sys.id=gLVzQlb09IeOeU181fwtz
6
+ body:
7
+ encoding: UTF-8
8
+ string: ''
9
+ headers:
10
+ X-Contentful-User-Agent:
11
+ - sdk contentful.rb/2.15.1; platform ruby/2.6.3; os macOS/18;
12
+ Authorization:
13
+ - Bearer 4d0f55d940975f78139daae5d965b463c0816e88ad16062d2c1ee3d6cb930521
14
+ Content-Type:
15
+ - application/vnd.contentful.delivery.v1+json
16
+ Accept-Encoding:
17
+ - gzip
18
+ Connection:
19
+ - close
20
+ Host:
21
+ - cdn.contentful.com
22
+ User-Agent:
23
+ - http.rb/4.2.0
24
+ response:
25
+ status:
26
+ code: 200
27
+ message: OK
28
+ headers:
29
+ Access-Control-Allow-Headers:
30
+ - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature
31
+ Access-Control-Allow-Methods:
32
+ - GET,HEAD,OPTIONS
33
+ Access-Control-Allow-Origin:
34
+ - "*"
35
+ Access-Control-Expose-Headers:
36
+ - Etag
37
+ Access-Control-Max-Age:
38
+ - '86400'
39
+ Cf-Environment-Id:
40
+ - master
41
+ Cf-Environment-Uuid:
42
+ - 7df33faa-1d21-4dea-83e9-9ebda5f47ba9
43
+ Cf-Organization-Id:
44
+ - 4SsuxQCaMaemfIms52Jr8s
45
+ Cf-Space-Id:
46
+ - facgnwwgj5fe
47
+ Content-Encoding:
48
+ - gzip
49
+ Content-Type:
50
+ - application/vnd.contentful.delivery.v1+json
51
+ Contentful-Api:
52
+ - cda_cached
53
+ Etag:
54
+ - W/"14036385179019726604"
55
+ Server:
56
+ - Contentful
57
+ X-Content-Type-Options:
58
+ - nosniff
59
+ X-Contentful-Region:
60
+ - us-east-1
61
+ Content-Length:
62
+ - '832'
63
+ Accept-Ranges:
64
+ - bytes
65
+ Date:
66
+ - Thu, 19 Dec 2019 09:54:02 GMT
67
+ Via:
68
+ - 1.1 varnish
69
+ Age:
70
+ - '0'
71
+ Connection:
72
+ - close
73
+ X-Served-By:
74
+ - cache-ams21023-AMS
75
+ X-Cache:
76
+ - MISS
77
+ X-Cache-Hits:
78
+ - '0'
79
+ Vary:
80
+ - Accept-Encoding
81
+ X-Contentful-Request-Id:
82
+ - e8767cee-9cbe-46be-8fab-47d35d6c5db7
83
+ body:
84
+ encoding: ASCII-8BIT
85
+ string: !binary |-
86
+ H4sIAAAAAAAAA81WW2+bMBR+769APK8J5NbSt25r127VqqZJq22qIgMHYkoMwiZZWuW/zxdIHEpINk3TUBRhG5/znet3Xo8Mw6RLap4Zr/yVL9gyBb4yz7MMLU2+t3onvmEJQzHft+WKPuOULyy5iPEMM3FkqTVmMBMCf0iBSmxFi9REU+QJVeUXalPDIjbkZonpBpNnU+jcPFw7eR4VmO+lxMoH2BfmBMgLyWIRRv0AhFXls1q/SzvVY6o74c3Dy13sWs413MLYPrWDBXvRxK9xXRCWLfUDLwPEwD8XbjE7lu0c251j2xlZzlnf5r9Wtzf4rl/IU//3LgCZ4ywhMyBCx34XKotmiDLIqh461L0Xms69LsxgjilOSJkzhWe9hDCOuYjYftyHYvugya1NgJykuRtjOuVhoRQY3Z8FceKhWBYDkOPxfXlhnShmgCH2N7UjTDQZZurOeKPQKDRqGUYxCWOQ+wfF71A/KIm1HhhEEXr46OZkNIiW6PLh5+XYuRrvd8MsjxkukaqiVsHUo1dT4EXEG5DL5rEp3zrs/BOVup34PWTzG5/ln/E1HV4NrShiCx09b1V6Vuo++KdIH++6wym6fQzpt9GXr/e9IB/M4waga9BPxZsyQ/w/yQYLWZZkh3RU5SmSsCHQJJ4jl2eilnNlJKS8t9nsA0M4rqZz0VorrbcxbgdFrDBSwdtNEv+XSc0lVI0bJl6c+6CRa7WKNnm5RcJF5dQR5N+oszqaXNfZLqJsqK4i3vV5r5fhZriQfW/rqIEye5w1rVZ/YOuUyQE3kOauK7tpc7djG6iTXzq8ve2gzybH1pOoaJv11FRMa0X+1NCTAFwS1Kck9oEYGbAMwxwyg88snJWMFIfUCJKkEqAAS16rtNI8E1Oh2W7jGQqBtjwWIEmvLQKsrSdTuzY/2l3PPUXd7kkHen3/xHVcO+gg1wPUCzpgn1ptBXOyhjlRMCcC5kTAbEVpWB1p6npZWVX4Rdjh2J2T7jZPihoQVryxUhL7Avtsyo/6asxV0sp/cwo4nAoqH/QdjYXEuc5KWwGSYoVbv6KZHDP+yNTtiUpZ0I5SCHcw+xbJCMpZHa2OfgEgI0mYCAwAAA==
87
+ http_version:
88
+ recorded_at: Thu, 19 Dec 2019 09:54:02 GMT
89
+ recorded_with: VCR 5.0.0