contentful 2.14.0 → 2.15.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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