-
1
require 'contentful/version'
-
1
require 'contentful/support'
-
1
require 'contentful/client'
-
1
require_relative 'base_resource'
-
1
require_relative 'array_like'
-
-
1
module Contentful
-
# Resource Class for Arrays (e.g. search results)
-
# @see _ https://www.contentful.com/developers/documentation/content-delivery-api/#arrays
-
# @note It also provides an #each method and includes Ruby's Enumerable module (gives you methods like #min, #first, etc)
-
1
class Array < BaseResource
-
# @private
-
1
DEFAULT_LIMIT = 100
-
-
1
include Contentful::ArrayLike
-
-
1
attr_reader :total, :limit, :skip, :items, :endpoint
-
-
1
def initialize(item = nil,
-
configuration = {
-
default_locale: Contentful::Client::DEFAULT_CONFIGURATION[:default_locale]
-
},
-
endpoint = '', *)
-
101
super(item, configuration)
-
-
101
@endpoint = endpoint
-
101
@total = item.fetch('total', nil)
-
101
@limit = item.fetch('limit', nil)
-
101
@skip = item.fetch('skip', nil)
-
101
@items = item.fetch('items', [])
-
end
-
-
# @private
-
1
def marshal_dump
-
3
super.merge(endpoint: endpoint)
-
end
-
-
# @private
-
1
def marshal_load(raw_object)
-
3
super
-
3
@endpoint = raw_object[:endpoint]
-
3
@total = raw.fetch('total', nil)
-
3
@limit = raw.fetch('limit', nil)
-
3
@skip = raw.fetch('skip', nil)
-
3
@items = raw.fetch('items', []).map do |item|
-
7
require_relative 'resource_builder'
-
ResourceBuilder.new(
-
item.raw,
-
raw_object[:configuration].merge(includes_for_single: Support.includes_from_response(raw, false)),
-
7
item.respond_to?(:localized) ? item.localized : false
-
7
).run
-
end
-
end
-
-
# @private
-
1
def inspect
-
2
"<#{repr_name} total=#{total} skip=#{skip} limit=#{limit}>"
-
end
-
-
# Simplifies pagination
-
#
-
# @return [Contentful::Array, false]
-
1
def next_page(client = nil)
-
1
return false if client.nil?
-
1
return false if items.first.nil?
-
-
1
new_skip = (skip || 0) + (limit || DEFAULT_LIMIT)
-
-
1
plurals = {
-
'Space' => 'spaces',
-
'ContentType' => 'content_types',
-
'Entry' => 'entries',
-
'Asset' => 'assets',
-
'Locale' => 'locales'
-
}
-
1
client.public_send(plurals[items.first.type], limit: limit, skip: new_skip)
-
end
-
end
-
end
-
1
module Contentful
-
# Useful methods for array-like resources that can be included if an
-
# :items property exists
-
1
module ArrayLike
-
1
include Enumerable
-
-
# Returns true for array-like resources
-
#
-
# @return [true]
-
1
def array?
-
true
-
end
-
-
# Delegates to items#each
-
#
-
# @yield [Contentful::Entry, Contentful::Asset]
-
1
def each_item(&block)
-
101
items.each(&block)
-
end
-
1
alias each each_item
-
-
# Delegates to items#empty?
-
#
-
# @return [Boolean]
-
1
def empty?
-
5
items.empty?
-
end
-
-
# Delegetes to items#size
-
#
-
# @return [Number]
-
1
def size
-
items.size
-
end
-
1
alias length size
-
-
# Delegates to items#[]
-
#
-
# @return [Contentful::Entry, Contentful::Asset]
-
1
def [](index)
-
items[index]
-
end
-
-
# Delegates to items#last
-
#
-
# @return [Contentful::Entry, Contentful::Asset]
-
1
def last
-
4
items.last
-
end
-
end
-
end
-
1
require_relative 'fields_resource'
-
1
require_relative 'file'
-
1
require_relative 'resource_references'
-
-
1
module Contentful
-
# Resource class for Asset.
-
# https://www.contentful.com/developers/documentation/content-delivery-api/#assets
-
1
class Asset < FieldsResource
-
1
include Contentful::ResourceReferences
-
-
# @private
-
1
def marshal_dump
-
19
{
-
configuration: @configuration,
-
raw: raw
-
}
-
end
-
-
# @private
-
1
def marshal_load(raw_object)
-
19
super(raw_object)
-
19
create_files!
-
19
define_asset_methods!
-
end
-
-
# @private
-
1
def inspect
-
"<#{repr_name} id='#{sys[:id]}' url='#{url}'>"
-
end
-
-
1
def initialize(*)
-
1482
super
-
1482
create_files!
-
1482
define_asset_methods!
-
end
-
-
# Generates a URL for the Contentful Image API
-
#
-
# @param [Hash] options
-
# @option options [Integer] :width
-
# @option options [Integer] :height
-
# @option options [String] :format
-
# @option options [String] :quality
-
# @option options [String] :focus
-
# @option options [String] :fit
-
# @option options [String] :fl File Layering - 'progressive'
-
# @option options [String] :background
-
# @see _ https://www.contentful.com/developers/documentation/content-delivery-api/#image-asset-resizing
-
#
-
# @return [String] Image API URL
-
1
def image_url(options = {})
-
17
query = build_query(options)
-
-
17
if query.empty?
-
15
file.url
-
else
-
2
"#{file.url}?#{URI.encode_www_form(query)}"
-
end
-
end
-
-
1
alias url image_url
-
-
1
private
-
-
1
def build_query(options)
-
{
-
w: options[:w] || options[:width],
-
h: options[:h] || options[:height],
-
fm: options[:fm] || options[:format],
-
q: options[:q] || options[:quality],
-
f: options[:f] || options[:focus],
-
bg: options[:bg] || options[:background],
-
r: options[:r] || options[:radius],
-
fit: options[:fit],
-
fl: options[:fl]
-
170
}.reject { |_k, v| v.nil? }
-
end
-
-
1
def create_files!
-
1501
file_json = raw.fetch('fields', {}).fetch('file', nil)
-
1501
return if file_json.nil?
-
-
2994
is_localized = file_json.keys.none? { |f| %w[fileName contentType details url].include? f }
-
1496
if is_localized
-
737
locales.each do |locale|
-
739
@fields[locale][:file] = ::Contentful::File.new(file_json[locale.to_s] || {}, @configuration)
-
end
-
else
-
759
@fields[internal_resource_locale][:file] = ::Contentful::File.new(file_json, @configuration)
-
end
-
end
-
-
1
def define_asset_methods!
-
1501
define_singleton_method :description do
-
fields.fetch(:description, nil)
-
end
-
-
1501
define_singleton_method :file do |wanted_locale = nil|
-
41
fields(wanted_locale)[:file]
-
end
-
end
-
end
-
end
-
1
require_relative 'support'
-
-
1
module Contentful
-
# Base definition of a Contentful Resource containing Sys properties
-
1
class BaseResource
-
1
attr_reader :raw, :default_locale, :sys
-
-
# rubocop:disable Metrics/ParameterLists
-
1
def initialize(item, configuration = {}, _localized = false, _includes = [], entries = {}, depth = 0, _errors = [])
-
8659
entries["#{item['sys']['type']}:#{item['sys']['id']}"] = self if entries && item.key?('sys')
-
8659
@raw = item
-
8659
@default_locale = configuration[:default_locale]
-
8659
@depth = depth
-
8659
@configuration = configuration
-
8659
@sys = hydrate_sys
-
-
8659
define_sys_methods!
-
end
-
-
# @private
-
1
def inspect
-
2
"<#{repr_name} id='#{sys[:id]}'>"
-
end
-
-
# Definition of equality
-
1
def ==(other)
-
5
self.class == other.class && sys[:id] == other.sys[:id]
-
end
-
-
# @private
-
1
def marshal_dump
-
7
{
-
configuration: @configuration,
-
raw: raw
-
}
-
end
-
-
# @private
-
1
def marshal_load(raw_object)
-
52
@raw = raw_object[:raw]
-
52
@configuration = raw_object[:configuration]
-
52
@default_locale = @configuration[:default_locale]
-
52
@sys = hydrate_sys
-
52
@depth = 0
-
52
define_sys_methods!
-
end
-
-
# Issues the request that was made to fetch this response again.
-
# Only works for Entry, Asset, ContentType and Space
-
1
def reload(client = nil)
-
1
return client.send(Support.snakify(self.class.name.split('::').last), id) unless client.nil?
-
-
1
false
-
end
-
-
1
private
-
-
1
def define_sys_methods!
-
8711
@sys.each do |k, v|
-
39226
define_singleton_method k do
-
18253
v
-
end
-
end
-
end
-
-
1
def hydrate_sys
-
8711
result = {}
-
8711
raw.fetch('sys', {}).each do |k, v|
-
39226
if %w[space contentType environment].include?(k)
-
5057
v = build_link(v)
-
34169
elsif %w[createdAt updatedAt deletedAt].include?(k)
-
6724
v = DateTime.parse(v)
-
end
-
39226
result[Support.snakify(k, @configuration[:use_camel_case]).to_sym] = v
-
end
-
8711
result
-
end
-
-
1
protected
-
-
1
def repr_name
-
4
self.class
-
end
-
-
1
def internal_resource_locale
-
7428
sys.fetch(:locale, nil) || default_locale
-
end
-
-
1
def build_link(item)
-
5213
require_relative 'link'
-
5213
::Contentful::Link.new(item, @configuration)
-
end
-
end
-
end
-
1
require_relative 'request'
-
1
require_relative 'response'
-
1
require_relative 'resource_builder'
-
1
require_relative 'sync'
-
1
require_relative 'content_type_cache'
-
-
1
require 'http'
-
1
require 'logger'
-
1
require 'rbconfig'
-
-
1
module Contentful
-
# The client object is initialized with a space and a key and then used
-
# for querying resources from this space.
-
# See README for details
-
1
class Client
-
# Default configuration for Contentful::Client
-
1
DEFAULT_CONFIGURATION = {
-
secure: true,
-
raise_errors: true,
-
dynamic_entries: :manual,
-
api_url: 'cdn.contentful.com',
-
api_version: 1,
-
environment: 'master',
-
authentication_mechanism: :header,
-
resource_builder: ResourceBuilder,
-
resource_mapping: {},
-
entry_mapping: {},
-
default_locale: 'en-US',
-
raw_mode: false,
-
gzip_encoded: true,
-
logger: false,
-
log_level: Logger::INFO,
-
proxy_host: nil,
-
proxy_username: nil,
-
proxy_password: nil,
-
proxy_port: nil,
-
max_rate_limit_retries: 1,
-
max_rate_limit_wait: 60,
-
max_include_resolution_depth: 20,
-
use_camel_case: false,
-
application_name: nil,
-
application_version: nil,
-
integration_name: nil,
-
integration_version: nil
-
}
-
-
1
attr_reader :configuration, :logger, :proxy
-
-
# Wraps the actual HTTP request via proxy
-
# @private
-
1
def self.get_http(url, query, headers = {}, proxy = {})
-
213
if proxy[:host]
-
HTTP[headers].via(proxy[:host], proxy[:port], proxy[:username], proxy[:password]).get(url, params: query)
-
else
-
213
HTTP[headers].get(url, params: query)
-
end
-
end
-
-
# @see _ https://github.com/contentful/contentful.rb#client-configuration-options
-
# @param [Hash] given_configuration
-
# @option given_configuration [String] :space Required
-
# @option given_configuration [String] :access_token Required
-
# @option given_configuration [String] :api_url Modifying this to 'preview.contentful.com' gives you access to our Preview API
-
# @option given_configuration [String] :api_version
-
# @option given_configuration [String] :default_locale
-
# @option given_configuration [String] :proxy_host
-
# @option given_configuration [String] :proxy_username
-
# @option given_configuration [String] :proxy_password
-
# @option given_configuration [Number] :proxy_port
-
# @option given_configuration [Number] :max_rate_limit_retries
-
# @option given_configuration [Number] :max_rate_limit_wait
-
# @option given_configuration [Number] :max_include_resolution_depth
-
# @option given_configuration [Boolean] :use_camel_case
-
# @option given_configuration [Boolean] :gzip_encoded
-
# @option given_configuration [Boolean] :raw_mode
-
# @option given_configuration [false, ::Logger] :logger
-
# @option given_configuration [::Logger::DEBUG, ::Logger::INFO, ::Logger::WARN, ::Logger::ERROR] :log_level
-
# @option given_configuration [Boolean] :raise_errors
-
# @option given_configuration [::Array<String>] :dynamic_entries
-
# @option given_configuration [::Hash<String, Contentful::Resource>] :resource_mapping
-
# @option given_configuration [::Hash<String, Contentful::Resource>] :entry_mapping
-
# @option given_configuration [String] :application_name
-
# @option given_configuration [String] :application_version
-
# @option given_configuration [String] :integration_name
-
# @option given_configuration [String] :integration_version
-
1
def initialize(given_configuration = {})
-
222
@configuration = default_configuration.merge(given_configuration)
-
222
normalize_configuration!
-
222
validate_configuration!
-
220
setup_logger
-
-
220
update_dynamic_entry_cache! if configuration[:dynamic_entries] == :auto
-
end
-
-
# @private
-
1
def setup_logger
-
220
@logger = configuration[:logger]
-
220
logger.level = configuration[:log_level] if logger
-
end
-
-
# @private
-
1
def proxy_params
-
219
{
-
host: configuration[:proxy_host],
-
port: configuration[:proxy_port],
-
username: configuration[:proxy_username],
-
password: configuration[:proxy_password]
-
}
-
end
-
-
# Returns the default configuration
-
# @private
-
1
def default_configuration
-
222
DEFAULT_CONFIGURATION.dup
-
end
-
-
# Gets the client's space
-
#
-
# @param [Hash] query
-
#
-
# @return [Contentful::Space]
-
1
def space(query = {})
-
9
Request.new(self, '', query).get
-
end
-
-
# Gets a specific content type
-
#
-
# @param [String] id
-
# @param [Hash] query
-
#
-
# @return [Contentful::ContentType]
-
1
def content_type(id, query = {})
-
23
Request.new(self, environment_url('/content_types'), query, id).get
-
end
-
-
# Gets a collection of content types
-
#
-
# @param [Hash] query
-
#
-
# @return [Contentful::Array<Contentful::ContentType>]
-
1
def content_types(query = {})
-
28
Request.new(self, environment_url('/content_types'), query).get
-
end
-
-
# Gets a specific entry
-
#
-
# @param [String] id
-
# @param [Hash] query
-
#
-
# @return [Contentful::Entry]
-
1
def entry(id, query = {})
-
42
normalize_select!(query)
-
42
query['sys.id'] = id
-
42
entries = Request.new(self, environment_url('/entries'), query).get
-
-
39
return entries if configuration[:raw_mode]
-
-
38
entries.first
-
end
-
-
# Gets a collection of entries
-
#
-
# @param [Hash] query
-
#
-
# @return [Contentful::Array<Contentful::Entry>]
-
1
def entries(query = {})
-
24
normalize_select!(query)
-
24
Request.new(self, environment_url('/entries'), query).get
-
end
-
-
# Gets a specific asset
-
#
-
# @param [String] id
-
# @param [Hash] query
-
#
-
# @return [Contentful::Asset]
-
1
def asset(id, query = {})
-
23
Request.new(self, environment_url('/assets'), query, id).get
-
end
-
-
# Gets a collection of assets
-
#
-
# @param [Hash] query
-
#
-
# @return [Contentful::Array<Contentful::Asset>]
-
1
def assets(query = {})
-
9
normalize_select!(query)
-
9
Request.new(self, environment_url('/assets'), query).get
-
end
-
-
# Gets a collection of locales for the current environment
-
#
-
# @param [Hash] query
-
#
-
# @return [Contentful::Array<Contentful::Locale>]
-
1
def locales(query = {})
-
1
Request.new(self, environment_url('/locales'), query).get
-
end
-
-
# Returns the base url for all of the client's requests
-
# @private
-
1
def base_url
-
203
"http#{configuration[:secure] ? 's' : ''}://#{configuration[:api_url]}/spaces/#{configuration[:space]}"
-
end
-
-
# Returns the url aware of the currently selected environment
-
# @private
-
1
def environment_url(path)
-
188
"/environments/#{configuration[:environment]}#{path}"
-
end
-
-
# Returns the formatted part of the X-Contentful-User-Agent header
-
# @private
-
1
def format_user_agent_header(key, values)
-
813
header = "#{key} #{values[:name]}"
-
813
header = "#{header}/#{values[:version]}" if values[:version]
-
813
"#{header};"
-
end
-
-
# Returns the X-Contentful-User-Agent sdk data
-
# @private
-
1
def sdk_info
-
261
{ name: 'contentful.rb', version: ::Contentful::VERSION }
-
end
-
-
# Returns the X-Contentful-User-Agent app data
-
# @private
-
1
def app_info
-
261
{ name: configuration[:application_name], version: configuration[:application_version] }
-
end
-
-
# Returns the X-Contentful-User-Agent integration data
-
# @private
-
1
def integration_info
-
261
{ name: configuration[:integration_name], version: configuration[:integration_version] }
-
end
-
-
# Returns the X-Contentful-User-Agent platform data
-
# @private
-
1
def platform_info
-
261
{ name: 'ruby', version: RUBY_VERSION }
-
end
-
-
# Returns the X-Contentful-User-Agent os data
-
# @private
-
1
def os_info
-
261
os_name = case ::RbConfig::CONFIG['host_os']
-
when /(cygwin|mingw|mswin|windows)/i then 'Windows'
-
261
when /(darwin|macruby|mac os)/i then 'macOS'
-
when /(linux|bsd|aix|solarix)/i then 'Linux'
-
end
-
261
{ name: os_name, version: Gem::Platform.local.version }
-
end
-
-
# Returns the X-Contentful-User-Agent
-
# @private
-
1
def contentful_user_agent
-
261
header = {
-
'sdk' => sdk_info,
-
'app' => app_info,
-
'integration' => integration_info,
-
'platform' => platform_info,
-
'os' => os_info
-
}
-
-
261
result = []
-
261
header.each do |key, values|
-
1305
next unless values[:name]
-
813
result << format_user_agent_header(key, values)
-
end
-
261
result.join(' ')
-
end
-
-
# Returns the headers used for the HTTP requests
-
# @private
-
1
def request_headers
-
225
headers = { 'X-Contentful-User-Agent' => contentful_user_agent }
-
225
headers['Authorization'] = "Bearer #{configuration[:access_token]}" if configuration[:authentication_mechanism] == :header
-
225
headers['Content-Type'] = "application/vnd.contentful.delivery.v#{configuration[:api_version].to_i}+json" if configuration[:api_version]
-
225
headers['Accept-Encoding'] = 'gzip' if configuration[:gzip_encoded]
-
225
headers
-
end
-
-
# Patches a query hash with the client configurations for queries
-
# @private
-
1
def request_query(query)
-
217
if configuration[:authentication_mechanism] == :query_string
-
1
query['access_token'] = configuration[:access_token]
-
end
-
217
query
-
end
-
-
# Get a Contentful::Request object
-
# Set second parameter to false to deactivate Resource building and
-
# return Response objects instead
-
#
-
# @private
-
1
def get(request, build_resource = true)
-
214
retries_left = configuration[:max_rate_limit_retries]
-
214
result = nil
-
214
begin
-
215
response = run_request(request)
-
-
215
return response if !build_resource || configuration[:raw_mode]
-
-
210
return fail_response(response) if response.status != :ok
-
-
201
result = do_build_resource(response)
-
rescue UnparsableResource => error
-
raise error if configuration[:raise_errors]
-
return error
-
rescue Contentful::RateLimitExceeded => rate_limit_error
-
2
reset_time = rate_limit_error.reset_time.to_i
-
2
if should_retry(retries_left, reset_time, configuration[:max_rate_limit_wait])
-
1
retries_left -= 1
-
1
logger.info(retry_message(retries_left, reset_time)) if logger
-
1
sleep(reset_time * Random.new.rand(1.0..1.2))
-
1
retry
-
end
-
-
1
raise
-
end
-
-
201
result
-
end
-
-
# @private
-
1
def retry_message(retries_left, reset_time)
-
1
message = 'Contentful API Rate Limit Hit! '
-
1
message += "Retrying - Retries left: #{retries_left}"
-
1
message += "- Time until reset (seconds): #{reset_time}"
-
1
message
-
end
-
-
# @private
-
1
def fail_response(response)
-
9
fail response.object if configuration[:raise_errors]
-
1
response.object
-
end
-
-
# @private
-
1
def should_retry(retries_left, reset_time, max_wait)
-
2
retries_left > 0 && max_wait > reset_time
-
end
-
-
# Runs request and parses Response
-
# @private
-
1
def run_request(request)
-
215
url = request.absolute? ? request.url : base_url + request.url
-
215
logger.info(request: { url: url, query: request.query, header: request_headers }) if logger
-
215
Response.new(
-
self.class.get_http(
-
url,
-
request_query(request.query),
-
request_headers,
-
proxy_params
-
), request
-
)
-
end
-
-
# Runs Resource Builder
-
# @private
-
1
def do_build_resource(response)
-
201
logger.debug(response: response) if logger
-
configuration[:resource_builder].new(
-
response.object,
-
configuration.merge(endpoint: response.request.endpoint),
-
201
(response.request.query || {}).fetch(:locale, nil) == '*',
-
0
-
201
).run
-
end
-
-
# Use this method together with the client's :dynamic_entries configuration.
-
# See README for details.
-
# @private
-
1
def update_dynamic_entry_cache!
-
17
return if configuration[:raw_mode]
-
17
content_types(limit: 1000).map do |ct|
-
43
ContentTypeCache.cache_set(configuration[:space], ct.id, ct)
-
end
-
end
-
-
# Use this method to manually register a dynamic entry
-
# See examples/dynamic_entries.rb
-
# @private
-
1
def register_dynamic_entry(key, klass)
-
1
ContentTypeCache.cache_set(configuration[:space], key, klass)
-
end
-
-
# Create a new synchronisation object
-
#
-
# @param [Hash, String] options Options or Sync URL
-
#
-
# @note You will need to call #each_page or #first_page on it
-
#
-
# @return [Contentful::Sync]
-
1
def sync(options = { initial: true })
-
43
Sync.new(self, options)
-
end
-
-
1
private
-
-
# If the query contains the :select operator, we enforce :sys properties.
-
# The SDK requires sys.type to function properly, but as other of our SDKs
-
# require more parts of the :sys properties, we decided that every SDK should
-
# include the complete :sys block to provide consistency accross our SDKs.
-
1
def normalize_select!(query)
-
75
return unless query.key?(:select)
-
-
10
query[:select] = query[:select].split(',').map(&:strip) if query[:select].is_a? String
-
21
query[:select] = query[:select].reject { |p| p.start_with?('sys.') }
-
10
query[:select] << 'sys' unless query[:select].include?('sys')
-
end
-
-
1
def normalize_configuration!
-
1110
%i[space access_token api_url default_locale].each { |s| configuration[s] = configuration[s].to_s }
-
222
configuration[:authentication_mechanism] = configuration[:authentication_mechanism].to_sym
-
end
-
-
1
def validate_configuration!
-
222
fail ArgumentError, 'You will need to initialize a client with a :space' if configuration[:space].empty?
-
221
fail ArgumentError, 'You will need to initialize a client with an :access_token' if configuration[:access_token].empty?
-
220
fail ArgumentError, 'The client configuration needs to contain an :api_url' if configuration[:api_url].empty?
-
220
fail ArgumentError, 'The client configuration needs to contain a :default_locale' if configuration[:default_locale].empty?
-
220
fail ArgumentError, 'The :api_version must be a positive number or nil' unless configuration[:api_version].to_i >= 0
-
220
fail ArgumentError, 'The authentication mechanism must be :header or :query_string' unless %i[header query_string].include?(
-
configuration[:authentication_mechanism]
-
)
-
220
fail ArgumentError, 'The :dynamic_entries mode must be :auto or :manual' unless %i[auto manual].include?(
-
configuration[:dynamic_entries]
-
)
-
end
-
end
-
end
-
1
require_relative 'location'
-
-
1
module Contentful
-
# Basic Coercion
-
1
class BaseCoercion
-
1
attr_reader :value, :options
-
1
def initialize(value, options = {})
-
6957
@value = value
-
6957
@options = options
-
end
-
-
# Coerces value
-
1
def coerce(*)
-
26
value
-
end
-
end
-
-
# Coercion for String Types
-
1
class StringCoercion < BaseCoercion
-
# Coerces value to String
-
1
def coerce(*)
-
3916
value.to_s
-
end
-
end
-
-
# Coercion for Text Types
-
1
class TextCoercion < StringCoercion; end
-
-
# Coercion for Symbol Types
-
1
class SymbolCoercion < StringCoercion; end
-
-
# Coercion for Integer Types
-
1
class IntegerCoercion < BaseCoercion
-
# Coerces value to Integer
-
1
def coerce(*)
-
996
value.to_i
-
end
-
end
-
-
# Coercion for Float Types
-
1
class FloatCoercion < BaseCoercion
-
# Coerces value to Float
-
1
def coerce(*)
-
1
value.to_f
-
end
-
end
-
-
# Coercion for Boolean Types
-
1
class BooleanCoercion < BaseCoercion
-
# Coerces value to Boolean
-
1
def coerce(*)
-
# rubocop:disable Style/DoubleNegation
-
!!value
-
# rubocop:enable Style/DoubleNegation
-
end
-
end
-
-
# Coercion for Date Types
-
1
class DateCoercion < BaseCoercion
-
# Coerces value to DateTime
-
1
def coerce(*)
-
1001
return nil if value.nil?
-
1000
return value if value.is_a?(Date)
-
-
999
DateTime.parse(value)
-
end
-
end
-
-
# Coercion for Location Types
-
1
class LocationCoercion < BaseCoercion
-
# Coerces value to Location
-
1
def coerce(*)
-
2
Location.new(value)
-
end
-
end
-
-
# Coercion for Object Types
-
1
class ObjectCoercion < BaseCoercion
-
# Coerces value to hash, symbolizing each key
-
1
def coerce(*)
-
7
JSON.parse(JSON.dump(value), symbolize_names: true)
-
end
-
end
-
-
# Coercion for Link Types
-
# Nothing should be done here as include resolution is handled within
-
# entries due to depth handling (explained within Entry).
-
# Only present as a placeholder for proper resolution within ContentType.
-
1
class LinkCoercion < BaseCoercion; end
-
-
# Coercion for Array Types
-
1
class ArrayCoercion < BaseCoercion
-
# Coerces value for each element
-
1
def coerce(*)
-
1007
value.map do |e|
-
1525
options[:coercion_class].new(e).coerce
-
end
-
end
-
end
-
-
# Coercion for StructuredText Types
-
1
class StructuredTextCoercion < BaseCoercion
-
# Resolves includes and removes unresolvable nodes
-
1
def coerce(configuration)
-
1
coerce_block(value, configuration)
-
end
-
-
1
private
-
-
1
def link?(node)
-
17
node['nodeClass'] == 'block' && node.key?('data')
-
end
-
-
1
def content_block?(node)
-
node['nodeClass'] == 'block' && node.key?('content')
-
end
-
-
1
def coerce_block(block, configuration)
-
1
return block unless block.is_a?(Hash) && block.key?('content')
-
-
1
invalid_nodes = []
-
1
block['content'].each_with_index do |node, index|
-
17
if link?(node)
-
17
link = coerce_link(node, configuration)
-
-
17
if !link.nil?
-
16
node['data'] = link
-
else
-
1
invalid_nodes << index
-
end
-
elsif content_block?(node)
-
node['content'] = coerce_block(node, configuration)
-
end
-
end
-
-
1
invalid_nodes.each do |index|
-
1
block['content'].delete_at(index)
-
end
-
-
1
block
-
end
-
-
1
def coerce_link(node, configuration)
-
17
return node unless node.key?('data') && node['data'].key?('target')
-
3
return node unless node['data']['target']['sys']['type'] == 'Link'
-
-
3
return nil if Support.unresolvable?(node['data']['target'], configuration[:errors])
-
-
2
resource = Support.resource_for_link(
-
node['data']['target'],
-
configuration[:includes_for_single]
-
)
-
-
ResourceBuilder.new(
-
resource,
-
configuration,
-
configuration[:localized],
-
configuration[:depth] + 1,
-
configuration[:errors]
-
2
).run
-
end
-
end
-
end
-
1
require_relative 'base_resource'
-
1
require_relative 'field'
-
1
require_relative 'support'
-
-
1
module Contentful
-
# Resource Class for Content Types
-
# https://www.contentful.com/developers/documentation/content-delivery-api/#content-types
-
1
class ContentType < BaseResource
-
1
attr_reader :name, :description, :fields, :display_field
-
-
1
def initialize(item, *)
-
115
super
-
-
115
@name = item.fetch('name', nil)
-
115
@description = item.fetch('description', nil)
-
673
@fields = item.fetch('fields', []).map { |field| Field.new(field) }
-
115
@display_field = item.fetch('displayField', nil)
-
end
-
-
# Field definition for field
-
1
def field_for(field_id)
-
24085
fields.detect { |f| Support.snakify(f.id) == Support.snakify(field_id) }
-
end
-
-
1
alias displayField display_field
-
-
1
protected
-
-
1
def repr_name
-
"#{super}[#{name}]"
-
end
-
end
-
end
-
1
module Contentful
-
# Cache for Content Types
-
1
class ContentTypeCache
-
1
@cache = {}
-
-
1
class << self
-
1
attr_reader :cache
-
end
-
-
# Clears the Content Type Cache
-
1
def self.clear!
-
20
@cache = {}
-
end
-
-
# Gets a Content Type from the Cache
-
1
def self.cache_get(space_id, content_type_id)
-
8263
@cache.fetch(space_id, {}).fetch(content_type_id.to_sym, nil)
-
end
-
-
# Sets a Content Type in the Cache
-
1
def self.cache_set(space_id, content_type_id, klass)
-
44
@cache[space_id] ||= {}
-
44
@cache[space_id][content_type_id.to_sym] = klass
-
end
-
end
-
end
-
1
require_relative 'base_resource'
-
-
1
module Contentful
-
# Resource class for deleted entries
-
# https://www.contentful.com/developers/documentation/content-delivery-api/http/#sync-item-types
-
1
class DeletedAsset < BaseResource; end
-
end
-
1
require_relative 'base_resource'
-
-
1
module Contentful
-
# Resource class for deleted entries
-
# https://www.contentful.com/developers/documentation/content-delivery-api/http/#sync-item-types
-
1
class DeletedEntry < BaseResource; end
-
end
-
1
require_relative 'fields_resource'
-
1
require_relative 'content_type_cache'
-
1
require_relative 'resource_references'
-
-
1
module Contentful
-
# Resource class for Entry.
-
# @see _ https://www.contentful.com/developers/documentation/content-delivery-api/#entries
-
1
class Entry < FieldsResource
-
1
include Contentful::ResourceReferences
-
-
# Returns true for resources that are entries
-
1
def entry?
-
4
true
-
end
-
-
1
private
-
-
1
def coerce(field_id, value, includes, errors, entries = {})
-
11203
if Support.link?(value) && !Support.unresolvable?(value, errors)
-
2914
return build_nested_resource(value, includes, entries, errors)
-
end
-
8289
return coerce_link_array(value, includes, errors, entries) if Support.link_array?(value)
-
-
8263
content_type_key = Support.snakify('contentType', @configuration[:use_camel_case])
-
8263
content_type = ContentTypeCache.cache_get(sys[:space].id, sys[content_type_key.to_sym].id)
-
-
8263
unless content_type.nil?
-
5435
content_type_field = content_type.field_for(field_id)
-
5435
coercion_configuration = @configuration.merge(
-
includes_for_single:
-
@configuration.fetch(:includes_for_single, []) + includes,
-
_entries_cache: entries,
-
localized: localized,
-
depth: @depth,
-
errors: errors
-
)
-
5435
return content_type_field.coerce(value, coercion_configuration) unless content_type_field.nil?
-
end
-
-
2831
super(field_id, value, includes, errors, entries)
-
end
-
-
1
def coerce_link_array(value, includes, errors, entries)
-
26
items = []
-
26
value.each do |link|
-
42
nested_resource = build_nested_resource(link, includes, entries, errors) unless Support.unresolvable?(link, errors)
-
42
items << nested_resource unless nested_resource.nil?
-
end
-
-
26
items
-
end
-
-
# Maximum include depth is 10 in the API, but we raise it to 20 (by default),
-
# in case one of the included items has a reference in an upper level,
-
# so we can keep the include chain for that object as well
-
# Any included object after the maximum include resolution depth will be just a Link
-
1
def build_nested_resource(value, includes, entries, errors)
-
2954
if @depth < @configuration.fetch(:max_include_resolution_depth, 20)
-
2802
resource = Support.resource_for_link(value, includes)
-
2802
return resolve_include(resource, includes, entries, errors) unless resource.nil?
-
end
-
-
156
build_link(value)
-
end
-
-
1
def resolve_include(resource, includes, entries, errors)
-
2798
require_relative 'resource_builder'
-
-
ResourceBuilder.new(
-
resource,
-
@configuration.merge(
-
includes_for_single:
-
@configuration.fetch(:includes_for_single, []) + includes,
-
_entries_cache: entries
-
),
-
localized,
-
@depth + 1,
-
errors
-
2798
).run
-
end
-
-
1
def known_link?(name)
-
117
field_name = name.to_sym
-
117
return true if known_contentful_object?(fields[field_name])
-
126
fields[field_name].is_a?(Enumerable) && fields[field_name].any? { |object| known_contentful_object?(object) }
-
end
-
-
1
def known_contentful_object?(object)
-
154
(object.is_a?(Contentful::Entry) || object.is_a?(Contentful::Asset))
-
end
-
-
1
protected
-
-
1
def repr_name
-
2
content_type_key = Support.snakify('contentType', @configuration[:use_camel_case]).to_sym
-
2
"#{super}[#{sys[content_type_key].id}]"
-
end
-
end
-
end
-
1
module Contentful
-
# All errors raised by the contentful gem are either instances of Contentful::Error
-
# or inherit from Contentful::Error
-
1
class Error < StandardError
-
1
attr_reader :response
-
-
1
def initialize(response)
-
65
@response = response
-
65
super best_available_message
-
end
-
-
# Shortcut for creating specialized error classes
-
# USAGE rescue Contentful::Error[404]
-
1
def self.[](error_status_code)
-
66
errors = {
-
400 => BadRequest,
-
401 => Unauthorized,
-
403 => AccessDenied,
-
404 => NotFound,
-
429 => RateLimitExceeded,
-
500 => ServerError,
-
502 => BadGateway,
-
503 => ServiceUnavailable
-
}
-
-
66
errors.key?(error_status_code) ? errors[error_status_code] : Error
-
end
-
-
1
protected
-
-
1
def default_error_message
-
13
"The following error was received: #{@response.raw.body}"
-
end
-
-
1
def handle_details(details)
-
10
details.to_s
-
end
-
-
1
def additional_info?
-
59
false
-
end
-
-
1
def additional_info
-
[]
-
end
-
-
1
def best_available_message
-
65
error_message = [
-
"HTTP status code: #{@response.raw.status}"
-
]
-
-
65
begin
-
65
response_json = @response.load_json
-
61
message = response_json.fetch('message', default_error_message)
-
61
details = response_json.fetch('details', nil)
-
61
request_id = response_json.fetch('requestId', nil)
-
-
61
error_message << "Message: #{message}"
-
61
error_message << "Details: #{handle_details(details)}" if details
-
61
error_message << "Request ID: #{request_id}" if request_id
-
rescue
-
4
error_message << "Message: #{default_error_message}"
-
end
-
-
65
error_message << additional_info if additional_info?
-
-
65
error_message.join("\n")
-
end
-
end
-
-
# 400
-
1
class BadRequest < Error
-
1
protected
-
-
1
def default_error_message
-
10
'The request was malformed or missing a required parameter.'
-
end
-
-
1
def handle_details(details)
-
7
return details if details.is_a?(String)
-
-
5
handle_detail = proc do |detail|
-
5
return detail if detail.is_a?(String)
-
3
detail.fetch('details', nil)
-
end
-
-
10
inner_details = details['errors'].map { |detail| handle_detail[detail] }.reject(&:nil?)
-
3
inner_details.join("\n\t")
-
end
-
end
-
-
# 401
-
1
class Unauthorized < Error
-
1
protected
-
-
1
def default_error_message
-
3
'The authorization token was invalid.'
-
end
-
end
-
-
# 403
-
1
class AccessDenied < Error
-
1
protected
-
-
1
def default_error_message
-
4
'The specified token does not have access to the requested resource.'
-
end
-
-
1
def handle_details(details)
-
2
"\n\tReasons:\n\t\t#{details['reasons'].join("\n\t\t")}"
-
end
-
end
-
-
# 404
-
1
class NotFound < Error
-
1
protected
-
-
1
def default_error_message
-
17
'The requested resource or endpoint could not be found.'
-
end
-
-
1
def handle_details(details)
-
12
return details if details.is_a?(String)
-
-
10
type = details['type'] || (details['sys'] || {})['type']
-
10
message = "The requested #{type} could not be found."
-
-
10
resource_id = details.fetch('id', nil)
-
10
message += " ID: #{resource_id}." if resource_id
-
-
10
message
-
end
-
end
-
-
# 429
-
1
class RateLimitExceeded < Error
-
# Rate Limit Reset Header Key
-
1
RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'
-
-
1
def reset_time?
-
# rubocop:disable Style/DoubleNegation
-
6
!!reset_time
-
# rubocop:enable Style/DoubleNegation
-
end
-
-
# Time until next available request, in seconds.
-
1
def reset_time
-
11
@reset_time ||= @response.raw[RATE_LIMIT_RESET_HEADER_KEY]
-
end
-
-
1
protected
-
-
1
def additional_info?
-
6
reset_time?
-
end
-
-
1
def additional_info
-
3
["Time until reset (seconds): #{reset_time}"]
-
end
-
-
1
def default_error_message
-
6
'Rate limit exceeded. Too many requests.'
-
end
-
end
-
-
# 500
-
1
class ServerError < Error
-
1
protected
-
-
1
def default_error_message
-
2
'Internal server error.'
-
end
-
end
-
-
# 502
-
1
class BadGateway < Error
-
1
protected
-
-
1
def default_error_message
-
2
'The requested space is hibernated.'
-
end
-
end
-
-
# 503
-
1
class ServiceUnavailable < Error
-
1
protected
-
-
1
def default_error_message
-
4
'The request was malformed or missing a required parameter.'
-
end
-
end
-
-
# Raised when response is no valid json
-
1
class UnparsableJson < Error
-
1
protected
-
-
1
def default_error_message
-
4
@response.error_message
-
end
-
end
-
-
# Raised when response is not parsable as a Contentful::Resource
-
1
class UnparsableResource < StandardError; end
-
end
-
1
require_relative 'location'
-
1
require_relative 'coercions'
-
-
1
module Contentful
-
# A ContentType's field schema
-
# See https://www.contentful.com/developers/documentation/content-management-api/#resources-content-types-fields
-
1
class Field
-
# Coercions from Contentful Types to Ruby native types
-
1
KNOWN_TYPES = {
-
'String' => StringCoercion,
-
'Text' => TextCoercion,
-
'Symbol' => SymbolCoercion,
-
'Integer' => IntegerCoercion,
-
'Number' => FloatCoercion,
-
'Boolean' => BooleanCoercion,
-
'Date' => DateCoercion,
-
'Location' => LocationCoercion,
-
'Object' => ObjectCoercion,
-
'Array' => ArrayCoercion,
-
'Link' => LinkCoercion,
-
'StructuredText' => StructuredTextCoercion
-
}
-
-
1
attr_reader :raw, :id, :name, :type, :link_type, :items, :required, :localized
-
-
1
def initialize(json)
-
642
@raw = json
-
642
@id = json.fetch('id', nil)
-
642
@name = json.fetch('name', nil)
-
642
@type = json.fetch('type', nil)
-
642
@link_type = json.fetch('linkType', nil)
-
642
@items = json.key?('items') ? Field.new(json.fetch('items', {})) : nil
-
642
@required = json.fetch('required', false)
-
642
@localized = json.fetch('localized', false)
-
end
-
-
# Coerces value to proper type
-
1
def coerce(value, configuration)
-
5432
return value if type.nil?
-
5432
return value if value.nil?
-
-
5425
options = {}
-
5425
options[:coercion_class] = KNOWN_TYPES[items.type] unless items.nil?
-
5425
KNOWN_TYPES[type].new(value, options).coerce(configuration)
-
end
-
end
-
end
-
1
require_relative 'support'
-
1
require_relative 'base_resource'
-
-
1
module Contentful
-
# Base definition of a Contentful Resource containing Field properties
-
1
class FieldsResource < BaseResource
-
1
attr_reader :localized
-
-
# rubocop:disable Metrics/ParameterLists
-
1
def initialize(item, _configuration, localized = false, includes = [], entries = {}, depth = 0, errors = [])
-
3135
super
-
-
3135
@localized = localized
-
3135
@fields = hydrate_fields(includes, entries, errors)
-
3135
define_fields_methods!
-
end
-
-
# Returns all fields of the asset
-
#
-
# @return [Hash] fields for Resource on selected locale
-
1
def fields(wanted_locale = nil)
-
3518
wanted_locale = internal_resource_locale if wanted_locale.nil?
-
3518
@fields.fetch(wanted_locale.to_s, {})
-
end
-
-
# Returns all fields of the asset with locales nested by field
-
#
-
# @return [Hash] fields for Resource grouped by field name
-
1
def fields_with_locales
-
5
remapped_fields = {}
-
5
locales.each do |locale|
-
8
fields(locale).each do |name, value|
-
38
remapped_fields[name] ||= {}
-
38
remapped_fields[name][locale.to_sym] = value
-
end
-
end
-
-
5
remapped_fields
-
end
-
-
# Provides a list of the available locales for a Resource
-
1
def locales
-
743
@fields.keys
-
end
-
-
# @private
-
1
def marshal_dump
-
26
{
-
configuration: @configuration,
-
raw: raw_with_links,
-
localized: localized
-
}
-
end
-
-
# @private
-
1
def marshal_load(raw_object)
-
45
super(raw_object)
-
45
@localized = raw_object[:localized]
-
45
@fields = hydrate_fields(raw_object[:configuration].fetch(:includes_for_single, []), {}, [])
-
45
define_fields_methods!
-
end
-
-
# @private
-
1
def raw_with_links
-
143
links = fields.keys.select { |property| known_link?(property) }
-
26
processed_raw = raw.clone
-
26
raw['fields'].each do |k, v|
-
131
links_key = Support.snakify(k, @configuration[:use_camel_case])
-
131
processed_raw['fields'][k] = links.include?(links_key.to_sym) ? send(links_key) : v
-
end
-
-
26
processed_raw
-
end
-
-
1
private
-
-
1
def define_fields_methods!
-
3180
fields.each do |k, v|
-
13533
define_singleton_method k do
-
277
v
-
end
-
end
-
end
-
-
1
def hydrate_localized_fields(includes, errors, entries)
-
1562
locale = internal_resource_locale
-
1562
result = { locale => {} }
-
1562
raw['fields'].each do |name, locales|
-
6614
locales.each do |loc, value|
-
7295
result[loc] ||= {}
-
7295
name = Support.snakify(name, @configuration[:use_camel_case])
-
7295
result[loc][name.to_sym] = coerce(
-
name,
-
value,
-
includes,
-
errors,
-
entries
-
)
-
end
-
end
-
-
1562
result
-
end
-
-
1
def hydrate_nonlocalized_fields(includes, errors, entries)
-
1611
result = { locale => {} }
-
1611
locale = internal_resource_locale
-
1611
raw['fields'].each do |name, value|
-
6934
name = Support.snakify(name, @configuration[:use_camel_case])
-
6934
result[locale][name.to_sym] = coerce(
-
name,
-
value,
-
includes,
-
errors,
-
entries
-
)
-
end
-
-
1611
result
-
end
-
-
1
def hydrate_fields(includes, entries, errors)
-
3180
return {} unless raw.key?('fields')
-
-
3173
if localized
-
1562
hydrate_localized_fields(includes, errors, entries)
-
else
-
1611
hydrate_nonlocalized_fields(includes, errors, entries)
-
end
-
end
-
-
1
protected
-
-
1
def coerce(_field_id, value, _includes, _errors, _entries)
-
5857
value
-
end
-
end
-
end
-
1
module Contentful
-
# An Assets's file info
-
1
class File
-
1
def initialize(json, configuration)
-
1498
@configuration = configuration
-
-
1498
define_fields!(json)
-
end
-
-
1
private
-
-
1
def define_fields!(json)
-
1498
json.each do |k, v|
-
5992
define_singleton_method Support.snakify(k, @configuration[:use_camel_case]) do
-
40
v
-
end
-
end
-
end
-
end
-
end
-
1
require_relative 'base_resource'
-
-
1
module Contentful
-
# Resource Class for Links
-
# https://www.contentful.com/developers/documentation/content-delivery-api/#links
-
1
class Link < BaseResource
-
# Queries contentful for the Resource the Link is refering to
-
# Takes an optional query hash
-
1
def resolve(client, query = {})
-
2
id_and_query = [(id unless link_type == 'Space')].compact + [query]
-
2
client.public_send(
-
Contentful::Support.snakify(link_type).to_sym,
-
*id_and_query
-
)
-
end
-
end
-
end
-
1
require_relative 'base_resource'
-
-
1
module Contentful
-
# A Locale definition as included in Space
-
# Read more about Localization at https://www.contentful.com/developers/documentation/content-delivery-api/#i18n
-
1
class Locale < BaseResource
-
1
attr_reader :code, :name, :default, :fallback_code
-
-
1
def initialize(item, *)
-
21
@code = item.fetch('code', nil)
-
21
@name = item.fetch('name', nil)
-
21
@default = item.fetch('default', false)
-
21
@fallback_code = item.fetch('fallbackCode', nil)
-
end
-
end
-
end
-
1
module Contentful
-
# Location Field Type
-
# You can directly query for them: https://www.contentful.com/developers/documentation/content-delivery-api/#search-filter-geo
-
1
class Location
-
1
attr_reader :lat, :lon
-
1
alias latitude lat
-
1
alias longitude lon
-
-
1
def initialize(json)
-
2
@lat = json.fetch('lat', nil)
-
2
@lon = json.fetch('lon', nil)
-
end
-
end
-
end
-
1
module Contentful
-
# This object represents a request that is to be made. It gets initialized by the client
-
# with domain specific logic. The client later uses the Request's #url and #query methods
-
# to execute the HTTP request.
-
1
class Request
-
1
attr_reader :client, :type, :query, :id, :endpoint
-
-
1
def initialize(client, endpoint, query = {}, id = nil)
-
227
@client = client
-
227
@endpoint = endpoint
-
-
227
@query = (normalize_query(query) if query && !query.empty?)
-
-
227
if id
-
61
@type = :single
-
61
@id = URI.escape(id)
-
else
-
166
@type = :multi
-
166
@id = nil
-
end
-
end
-
-
# Returns the final URL, relative to a contentful space
-
1
def url
-
220
"#{@endpoint}#{@type == :single ? "/#{id}" : ''}"
-
end
-
-
# Delegates the actual HTTP work to the client
-
1
def get
-
207
client.get(self)
-
end
-
-
# Returns true if endpoint is an absolute url
-
1
def absolute?
-
215
@endpoint.start_with?('http')
-
end
-
-
# Returns a new Request object with the same data
-
1
def copy
-
Marshal.load(Marshal.dump(self))
-
end
-
-
1
private
-
-
1
def normalize_query(query)
-
118
Hash[
-
query.map do |key, value|
-
156
[
-
key.to_sym,
-
156
value.is_a?(::Array) ? value.join(',') : value
-
]
-
end
-
]
-
end
-
end
-
end
-
1
require_relative 'error'
-
1
require_relative 'space'
-
1
require_relative 'content_type'
-
1
require_relative 'entry'
-
1
require_relative 'asset'
-
1
require_relative 'array'
-
1
require_relative 'link'
-
1
require_relative 'deleted_entry'
-
1
require_relative 'deleted_asset'
-
1
require_relative 'locale'
-
-
1
module Contentful
-
# Transforms a Contentful::Response into a Contentful::Resource or a Contentful::Error
-
# See example/resource_mapping.rb for advanced usage
-
1
class ResourceBuilder
-
# Default Resource Mapping
-
# @see _ README for more information on Resource Mapping
-
1
DEFAULT_RESOURCE_MAPPING = {
-
'Space' => Space,
-
'ContentType' => ContentType,
-
'Entry' => Entry,
-
'Asset' => Asset,
-
'Array' => Array,
-
'Link' => Link,
-
'DeletedEntry' => DeletedEntry,
-
'DeletedAsset' => DeletedAsset,
-
'Locale' => Locale
-
}
-
# Default Entry Mapping
-
# @see _ README for more information on Entry Mapping
-
1
DEFAULT_ENTRY_MAPPING = {}
-
-
1
attr_reader :json, :default_locale, :endpoint, :depth, :localized, :resource_mapping, :entry_mapping, :resource
-
-
1
def initialize(json, configuration = {}, localized = false, depth = 0, errors = [])
-
3011
@json = json
-
3011
@default_locale = configuration.fetch(:default_locale, ::Contentful::Client::DEFAULT_CONFIGURATION[:default_locale])
-
3011
@resource_mapping = default_resource_mapping.merge(configuration.fetch(:resource_mapping, {}))
-
3011
@entry_mapping = default_entry_mapping.merge(configuration.fetch(:entry_mapping, {}))
-
3011
@includes_for_single = configuration.fetch(:includes_for_single, [])
-
3011
@localized = localized
-
3011
@depth = depth
-
3011
@endpoint = configuration.fetch(:endpoint, nil)
-
3011
@configuration = configuration
-
3011
@resource_cache = configuration[:_entries_cache] || {}
-
3011
@errors = errors
-
end
-
-
# Starts the parsing process.
-
#
-
# @return [Contentful::Resource, Contentful::Error]
-
1
def run
-
3011
return build_array if array?
-
2867
build_single
-
rescue UnparsableResource => error
-
error
-
end
-
-
1
private
-
-
1
def build_array
-
144
includes = fetch_includes || @includes_for_single
-
144
errors = fetch_errors || @errors
-
-
144
result = json['items'].map do |item|
-
439
next if Support.unresolvable?(item, errors)
-
439
build_item(item, includes, errors)
-
end
-
144
array_class = fetch_array_class
-
144
array_class.new(json.dup.merge('items' => result), @configuration, endpoint)
-
end
-
-
1
def build_single
-
2867
return if Support.unresolvable?(json, @errors)
-
2867
includes = @includes_for_single
-
2867
build_item(json, includes, @errors)
-
end
-
-
1
def build_item(item, includes = [], errors = [])
-
3306
buildables = %w[Entry Asset ContentType Space DeletedEntry DeletedAsset Locale]
-
8573
item_type = buildables.detect { |b| b.to_s == item['sys']['type'] }
-
3306
fail UnparsableResource, 'Item type is not known, could not parse' if item_type.nil?
-
3306
item_class = resource_class(item)
-
-
3306
reuse_entries = @configuration.fetch(:reuse_entries, false)
-
3306
resource_cache = @resource_cache ? @resource_cache : {}
-
-
3306
id = "#{item['sys']['type']}:#{item['sys']['id']}"
-
3306
resource = if reuse_entries && resource_cache.key?(id)
-
2
resource_cache[id]
-
else
-
3304
item_class.new(item, @configuration, localized?, includes, resource_cache, depth, errors)
-
end
-
-
3306
resource
-
end
-
-
1
def fetch_includes
-
144
Support.includes_from_response(json)
-
end
-
-
1
def fetch_errors
-
144
json.fetch('errors', [])
-
end
-
-
1
def resource_class(item)
-
3306
return fetch_custom_resource_class(item) if %w[Entry DeletedEntry Asset DeletedAsset].include?(item['sys']['type'])
-
127
resource_mapping[item['sys']['type']]
-
end
-
-
1
def fetch_custom_resource_class(item)
-
3179
case item['sys']['type']
-
when 'Entry'
-
1655
resource_class = entry_mapping[item['sys']['contentType']['sys']['id']]
-
1655
return resource_class unless resource_class.nil?
-
-
1632
fetch_custom_resource_mapping(item, 'Entry', Entry)
-
when 'Asset'
-
1482
fetch_custom_resource_mapping(item, 'Asset', Asset)
-
when 'DeletedEntry'
-
6
fetch_custom_resource_mapping(item, 'DeletedEntry', DeletedEntry)
-
when 'DeletedAsset'
-
36
fetch_custom_resource_mapping(item, 'DeletedAsset', DeletedAsset)
-
end
-
end
-
-
1
def fetch_custom_resource_mapping(item, type, default_class)
-
3156
resources = resource_mapping[type]
-
3156
return default_class if resources.nil?
-
-
3156
return resources if resources.is_a?(Class)
-
return resources[item] if resources.respond_to?(:call)
-
-
default_class
-
end
-
-
1
def fetch_array_class
-
144
return SyncPage if sync?
-
100
::Contentful::Array
-
end
-
-
1
def localized?
-
3304
return true if @localized
-
1968
return true if array? && sync?
-
1700
false
-
end
-
-
1
def array?
-
4979
json.fetch('sys', {}).fetch('type', '') == 'Array'
-
end
-
-
1
def sync?
-
578
json.fetch('nextSyncUrl', nil) || json.fetch('nextPageUrl', nil)
-
end
-
-
# The default mapping for #detect_resource_class
-
1
def default_resource_mapping
-
3011
DEFAULT_RESOURCE_MAPPING.dup
-
end
-
-
# The default entry mapping
-
1
def default_entry_mapping
-
3011
DEFAULT_ENTRY_MAPPING.dup
-
end
-
end
-
end
-
1
module Contentful
-
# Method to retrieve references (incoming links) for a given entry or asset
-
1
module ResourceReferences
-
# Gets a collection of entries which links to current entry
-
#
-
# @param [Contentful::Client] client
-
# @param [Hash] query
-
#
-
# @return [Contentful::Array<Contentful::Entry>, false]
-
1
def incoming_references(client = nil, query = {})
-
3
return false unless client
-
-
3
query = is_a?(Contentful::Entry) ? query.merge(links_to_entry: id) : query.merge(links_to_asset: id)
-
-
3
client.entries(query)
-
end
-
end
-
end
-
1
require_relative 'base_resource'
-
1
require_relative 'locale'
-
-
1
module Contentful
-
# Resource class for Space.
-
# https://www.contentful.com/developers/documentation/content-delivery-api/#spaces
-
1
class Space < BaseResource
-
1
attr_reader :name, :locales
-
-
1
def initialize(item, *)
-
9
super
-
-
9
@name = item.fetch('name', nil)
-
27
@locales = item.fetch('locales', []).map { |locale| Locale.new(locale) }
-
end
-
-
# @private
-
1
def reload(client = nil)
-
return client.space unless client.nil?
-
-
false
-
end
-
end
-
end
-
1
module Contentful
-
# Utility methods used by the contentful gem
-
1
module Support
-
1
class << self
-
# Transforms CamelCase into snake_case (taken from zucker)
-
#
-
# @param [String] object camelCaseName
-
# @param [Boolean] skip if true, skips returns original object
-
#
-
# @return [String] snake_case_name
-
1
def snakify(object, skip = false)
-
105145
return object if skip
-
-
String(object)
-
.gsub(/::/, '/')
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
-
.tr('-', '_')
-
103286
.downcase
-
end
-
-
1
def unresolvable?(value, errors)
-
6265
return true if value.nil?
-
-
6281
errors.any? { |i| i.fetch('details', {}).fetch('id', nil) == value['sys']['id'] }
-
end
-
-
# Checks if value is a link
-
#
-
# @param value
-
#
-
# @return [true, false]
-
1
def link?(value)
-
12685
value.is_a?(::Hash) &&
-
value.fetch('sys', {}).fetch('type', '') == 'Link'
-
end
-
-
# Checks if value is an array of links
-
#
-
# @param value
-
#
-
# @return [true, false]
-
1
def link_array?(value)
-
8289
return link?(value[0]) if value.is_a?(::Array) && !value.empty?
-
-
6807
false
-
end
-
-
# Returns the resource that matches the link
-
#
-
# @param [Hash] link
-
# @param [::Array] includes
-
#
-
# @return [Hash]
-
1
def resource_for_link(link, includes)
-
2804
includes.detect do |i|
-
17398
i['sys']['id'] == link['sys']['id'] &&
-
i['sys']['type'] == link['sys']['linkType']
-
end
-
end
-
-
# Returns combined include array from an API Response
-
#
-
# @param [Hash] json JSON Response
-
# @param [Bool] raw Response pre-proccessed?
-
#
-
# @return [Array]
-
1
def includes_from_response(json, raw = true)
-
151
includes = if raw
-
144
json['items'].dup
-
else
-
7
json['items'].map(&:raw)
-
end
-
-
151
%w[Entry Asset].each do |type|
-
302
if json.fetch('includes', {}).key?(type)
-
98
includes.concat(json['includes'].fetch(type, []))
-
end
-
end
-
-
151
includes
-
end
-
end
-
end
-
end
-
1
require_relative 'resource_builder'
-
1
require_relative 'deleted_entry'
-
1
require_relative 'deleted_asset'
-
1
require_relative 'sync_page'
-
-
1
module Contentful
-
# Resource class for Sync.
-
# @see _ https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization
-
1
class Sync
-
1
attr_reader :next_sync_url
-
-
1
def initialize(client, options_or_url)
-
43
@client = client
-
43
@next_sync_url = nil
-
43
@first_page_options_or_url = options_or_url
-
end
-
-
# Iterates over all pages of the current sync
-
#
-
# @note Please Keep in Mind: Iterating fires a new request for each page
-
#
-
# @yield [Contentful::SyncPage]
-
1
def each_page
-
2
page = first_page
-
2
yield page if block_given?
-
-
2
until completed?
-
2
page = page.next_page
-
2
yield page if block_given?
-
end
-
end
-
-
# Returns the first sync result page
-
#
-
# @return [Contentful::SyncPage]
-
1
def first_page
-
31
get(@first_page_options_or_url)
-
end
-
-
# Returns false as long as last sync page has not been reached
-
#
-
# @return [Boolean]
-
1
def completed?
-
# rubocop:disable Style/DoubleNegation
-
4
!!next_sync_url
-
# rubocop:enable Style/DoubleNegation
-
end
-
-
# Directly iterates over all resources that have changed
-
#
-
# @yield [Contentful::Entry, Contentful::Asset]
-
1
def each_item(&block)
-
1
each_page do |page|
-
2
page.each_item(&block)
-
end
-
end
-
-
# @private
-
1
def get(options_or_url)
-
47
page = fetch_page(options_or_url)
-
-
47
return page if @client.configuration[:raw_mode]
-
-
44
link_page_to_sync! page
-
44
update_sync_state_from! page
-
-
44
page
-
end
-
-
1
private
-
-
1
def fetch_page(options_or_url)
-
47
return Request.new(@client, options_or_url).get if options_or_url.is_a? String
-
30
Request.new(@client, @client.environment_url('/sync'), options_or_url).get
-
end
-
-
1
def link_page_to_sync!(page)
-
44
page.instance_variable_set :@sync, self
-
end
-
-
1
def update_sync_state_from!(page)
-
44
@next_sync_url = page.next_sync_url
-
end
-
end
-
end
-
1
require_relative 'base_resource'
-
1
require_relative 'array_like'
-
-
1
module Contentful
-
# Wrapper Class for Sync results
-
1
class SyncPage < BaseResource
-
1
include Contentful::ArrayLike
-
-
1
attr_reader :sync, :items, :next_sync_url, :next_page_url
-
-
1
def initialize(item,
-
configuration = {
-
default_locale: Contentful::Client::DEFAULT_CONFIGURATION[:default_locale]
-
}, *)
-
44
super(item, configuration, true)
-
-
44
@items = item.fetch('items', [])
-
44
@next_sync_url = item.fetch('nextSyncUrl', nil)
-
44
@next_page_url = item.fetch('nextPageUrl', nil)
-
end
-
-
# @private
-
1
def inspect
-
"<#{repr_name} next_sync_url='#{next_sync_url}' last_page=#{last_page?}>"
-
end
-
-
# Requests next sync page from API
-
#
-
# @return [Contentful::SyncPage, void]
-
1
def next_page
-
5
sync.get(next_page_url) if next_page?
-
end
-
-
# Returns wether there is a next sync page
-
#
-
# @return [Boolean]
-
1
def next_page?
-
# rubocop:disable Style/DoubleNegation
-
7
!!next_page_url
-
# rubocop:enable Style/DoubleNegation
-
end
-
-
# Returns wether it is the last sync page
-
#
-
# @return [Boolean]
-
1
def last_page?
-
2
!next_page_url
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Asset do
-
33
let(:asset) { vcr('asset') { create_client.asset('nyancat') } }
-
-
1
describe 'SystemProperties' do
-
1
it 'has a #sys getter returning a hash with symbol keys' do
-
1
expect(asset.sys).to be_a Hash
-
1
expect(asset.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it 'has #id' do
-
1
expect(asset.id).to eq 'nyancat'
-
end
-
-
1
it 'has #type' do
-
1
expect(asset.type).to eq 'Asset'
-
end
-
-
1
it 'has #space' do
-
1
expect(asset.space).to be_a Contentful::Link
-
end
-
-
1
it 'has #created_at' do
-
1
expect(asset.created_at).to be_a DateTime
-
end
-
-
1
it 'has #updated_at' do
-
1
expect(asset.updated_at).to be_a DateTime
-
end
-
-
1
it 'has #revision' do
-
1
expect(asset.revision).to eq 1
-
end
-
end
-
-
1
describe 'Fields' do
-
1
it 'has #title' do
-
1
expect(asset.title).to eq 'Nyan Cat'
-
end
-
-
1
it 'could have #description' do
-
1
expect(asset).to respond_to :description
-
end
-
-
1
it 'has #file' do
-
1
expect(asset.file).to be_a Contentful::File
-
end
-
end
-
-
1
describe '#image_url' do
-
1
it 'returns #url of #file without parameter' do
-
1
expect(asset.image_url).to eq asset.file.url
-
end
-
-
1
it 'adds image options if given' do
-
1
url = asset.image_url(width: 100, format: 'jpg', quality: 50, focus: 'top_right', fit: 'thumb', fl: 'progressive', background: 'rgb:ff0000')
-
1
expect(url).to include asset.file.url
-
1
expect(url).to include '?w=100&fm=jpg&q=50&f=top_right&bg=rgb%3Aff0000&fit=thumb&fl=progressive'
-
end
-
end
-
-
1
describe '#url' do
-
1
it 'returns #url of #file without parameter' do
-
1
expect(asset.url).to eq asset.file.url
-
end
-
-
1
it 'adds image options if given' do
-
1
url = asset.url(width: 100, format: 'jpg', quality: 50, focus: 'top_right', fit: 'thumb', fl: 'progressive', background: 'rgb:ff0000')
-
1
expect(url).to include asset.file.url
-
1
expect(url).to include '?w=100&fm=jpg&q=50&f=top_right&bg=rgb%3Aff0000&fit=thumb&fl=progressive'
-
end
-
end
-
-
1
it 'can be marshalled' do
-
1
marshalled = Marshal.dump(asset)
-
1
unmarshalled = Marshal.load(marshalled)
-
-
1
expect(unmarshalled.title).to eq 'Nyan Cat'
-
1
expect(unmarshalled.file).to be_a Contentful::File
-
end
-
-
-
1
describe 'incoming links' do
-
3
let(:client) { create_client }
-
-
1
it 'will fetch entries referencing the asset using a query' do
-
1
vcr('entry/search_link_to_asset') {
-
1
entries = client.entries(links_to_asset: 'nyancat')
-
1
expect(entries).not_to be_empty
-
1
expect(entries.count).to eq 1
-
1
expect(entries.first.id).to eq 'nyancat'
-
}
-
end
-
-
1
it 'will fetch entries referencing the entry using instance method' do
-
1
vcr('entry/search_link_to_asset') {
-
1
entries = asset.incoming_references client
-
1
expect(entries).not_to be_empty
-
1
expect(entries.count).to eq 1
-
1
expect(entries.first.id).to eq 'nyancat'
-
}
-
end
-
-
end
-
-
1
describe 'select operator' do
-
5
let(:client) { create_client }
-
-
1
context 'with sys sent' do
-
1
it 'properly creates an entry' do
-
1
vcr('asset/select_only_sys') {
-
1
asset = client.assets(select: ['sys']).first
-
1
expect(asset.fields).to be_empty
-
1
expect(asset.sys).not_to be_empty
-
}
-
end
-
-
1
it 'can contain only one field' do
-
1
vcr('asset/select_one_field') {
-
1
asset = client.assets(select: ['sys', 'fields.file']).first
-
1
expect(asset.fields.keys).to eq([:file])
-
}
-
end
-
end
-
-
1
context 'without sys sent' do
-
1
it 'will enforce sys anyway' do
-
1
vcr('asset/select_no_sys') {
-
1
asset = client.assets(select: ['fields'], 'sys.id' => 'nyancat').first
-
-
1
expect(asset.id).to eq 'nyancat'
-
1
expect(asset.sys).not_to be_empty
-
}
-
end
-
-
1
it 'works with empty array as well, as sys is enforced' do
-
1
vcr('asset/select_empty_array') {
-
1
asset = client.assets(select: [], 'sys.id' => 'nyancat').first
-
-
1
expect(asset.id).to eq 'nyancat'
-
1
expect(asset.sys).not_to be_empty
-
}
-
end
-
end
-
end
-
-
1
describe 'issues' do
-
1
it 'serializes files correctly for every locale - #129' do
-
1
vcr('assets/issues_129') {
-
1
client = create_client(
-
space: 'bht13amj0fva',
-
access_token: 'bb703a05e107148bed6ee246a9f6b3678c63fed7335632eb68fe1b689c801534'
-
)
-
-
1
asset = client.assets('sys.id' => '14bZJKTr6AoaGyeg4kYiWq', locale: '*').first
-
-
1
expect(asset.file).to be_a ::Contentful::File
-
1
expect(asset.file.file_name).to eq 'Flag_of_the_United_States.svg'
-
-
1
expect(asset.fields[:file]).to be_a ::Contentful::File
-
1
expect(asset.fields[:file].file_name).to eq 'Flag_of_the_United_States.svg'
-
-
1
expect(asset.fields('es')[:file]).to be_a ::Contentful::File
-
1
expect(asset.fields('es')[:file].file_name).to eq 'Flag_of_Spain.svg'
-
}
-
end
-
-
1
it 'properly serializes files for non-default locales on localized requests - jekyll-contentful-data-import #46' do
-
1
vcr('assets/issues_jekyll_46') {
-
1
client = create_client(
-
space: 'bht13amj0fva',
-
access_token: 'bb703a05e107148bed6ee246a9f6b3678c63fed7335632eb68fe1b689c801534',
-
)
-
-
1
asset = client.assets('sys.id' => '14bZJKTr6AoaGyeg4kYiWq', locale: 'es').first
-
-
1
expect(asset.file).to be_a ::Contentful::File
-
1
expect(asset.file.file_name).to eq 'Flag_of_Spain.svg'
-
}
-
end
-
end
-
-
1
describe 'camelCase' do
-
1
it 'properties now are accessed with camelcase' do
-
1
vcr('asset') {
-
1
asset = create_client(use_camel_case: true).asset('nyancat')
-
1
expect(asset.file.fileName).to eq 'Nyan_cat_250px_frame.png'
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe 'Auto-include resources' do
-
3
let(:entries) { vcr('entries') { create_client.entries } } # entries come with asset includes
-
-
1
it 'replaces Contentful::Links which are actually included with the resource' do
-
1
asset = entries.items[1].fields[:image]
-
-
1
expect(asset).not_to be_a Contentful::Link
-
1
expect(asset).to be_a Contentful::Asset
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Client do
-
1
describe '#get' do
-
9
let(:client) { create_client }
-
2
let(:proxy_client) { create_client(proxy_host: '183.207.232.194',
-
proxy_port: 8080,
-
secure: false) }
-
9
let(:request) { Contentful::Request.new(nil, client.environment_url('/content_types'), nil, 'cat') }
-
-
1
it 'uses #base_url' do
-
1
expect(client).to receive(:base_url).and_call_original
-
-
1
vcr('content_type') {
-
1
client.get(request)
-
}
-
end
-
-
1
it 'uses #request_headers' do
-
1
expect(client).to receive(:request_headers).and_call_original
-
2
vcr('content_type') { client.get(request) }
-
end
-
-
1
it 'uses Request#url' do
-
1
expect(request).to receive(:url).and_call_original
-
2
vcr('content_type') { client.get(request) }
-
end
-
-
1
it 'uses Request#query' do
-
1
expect(request).to receive(:query).twice.and_call_original
-
2
vcr('content_type') { client.get(request) }
-
end
-
-
1
it 'calls #get_http' do
-
2
expect(client.class).to receive(:get_http).with(client.base_url + request.url, request.query, client.request_headers, client.proxy_params) { raw_fixture('content_type') }
-
1
client.get(request)
-
end
-
-
1
it 'calls #get_http via proxy' do
-
2
expect(proxy_client.class).to receive(:get_http).with(proxy_client.base_url + request.url, request.query, proxy_client.request_headers, proxy_client.proxy_params) { raw_fixture('content_type') }
-
1
proxy_client.get(request)
-
1
expect(proxy_client.proxy_params[:host]).to eq '183.207.232.194'
-
1
expect(proxy_client.proxy_params[:port]).to eq 8080
-
end
-
-
1
describe 'build_resources parameter' do
-
1
it 'returns Contentful::Resource object if second parameter is true [default]' do
-
2
res = vcr('content_type') { client.get(request) }
-
1
expect(res).to be_a Contentful::BaseResource
-
end
-
-
1
it 'returns a Contentful::Response object if second parameter is not true' do
-
2
res = vcr('content_type') { client.get(request, false) }
-
1
expect(res).to be_a Contentful::Response
-
end
-
end
-
-
end
-
-
1
describe '#sync' do
-
1
it 'creates a new Sync object' do
-
1
expect(create_client.sync).to be_a Contentful::Sync
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe 'Client Configuration Options' do
-
1
describe ':space' do
-
1
it 'is required' do
-
1
expect do
-
1
Contentful::Client.new(access_token: 'b4c0n73n7fu1')
-
end.to raise_error(ArgumentError)
-
end
-
end
-
-
1
describe ':access_token' do
-
1
it 'is required' do
-
1
expect do
-
1
Contentful::Client.new(space: 'cfexampleapi')
-
end.to raise_error(ArgumentError)
-
end
-
end
-
-
1
describe ':secure' do
-
1
it 'will use https [default]' do
-
1
expect(
-
create_client.base_url
-
).to start_with 'https://'
-
end
-
-
1
it 'will use http when secure set to false' do
-
1
expect(
-
create_client(secure: false).base_url
-
).to start_with 'http://'
-
end
-
end
-
-
1
describe ':raise_errors' do
-
1
it 'will raise response errors if set to true [default]' do
-
1
expect_vcr('not found')do
-
1
create_client.content_type 'not found'
-
end.to raise_error Contentful::NotFound
-
end
-
-
1
it 'will not raise response errors if set to false' do
-
1
res = nil
-
-
1
expect_vcr('not found')do
-
1
res = create_client(raise_errors: false).content_type 'not found'
-
end.not_to raise_error
-
1
expect(res).to be_instance_of Contentful::NotFound
-
end
-
end
-
-
1
describe ':dynamic_entries' do
-
1
before :each do
-
6
Contentful::ContentTypeCache.clear!
-
end
-
-
1
it 'will create dynamic entries if dynamic_entry_cache is not empty' do
-
1
client = create_client(dynamic_entries: :manual)
-
2
vcr('entry_cache') { client.update_dynamic_entry_cache! }
-
2
entry = vcr('nyancat') { client.entry('nyancat') }
-
-
1
expect(entry).to be_a Contentful::Entry
-
end
-
-
1
context ':auto' do
-
1
it 'will call update dynamic_entry_cache on start-up' do
-
1
vcr('entry_cache') do
-
1
create_client(dynamic_entries: :auto)
-
end
-
1
expect(Contentful::ContentTypeCache.cache).not_to be_empty
-
end
-
end
-
-
1
context ':manual' do
-
1
it 'will not call #update_dynamic_entry_cache! on start-up' do
-
1
create_client(dynamic_entries: :manual)
-
1
expect(Contentful::ContentTypeCache.cache).to be_empty
-
end
-
end
-
-
1
describe '#update_dynamic_entry_cache!' do
-
3
let(:client) { create_client(dynamic_entries: :manual) }
-
-
1
it 'will fetch all content_types' do
-
2
expect(client).to receive(:content_types).with(limit: 1000) { {} }
-
1
client.update_dynamic_entry_cache!
-
end
-
-
1
it 'will save dynamic entries in @dynamic_entry_cache' do
-
1
vcr('entry_cache')do
-
1
client.update_dynamic_entry_cache!
-
end
-
1
expect(Contentful::ContentTypeCache.cache).not_to be_empty
-
end
-
end
-
-
1
describe '#register_dynamic_entry' do
-
2
let(:client) { create_client(dynamic_entries: :manual) }
-
-
1
it 'can be used to register a dynamic entry manually' do
-
2
cat = vcr('content_type') { client.content_type 'cat' }
-
1
client.register_dynamic_entry 'cat', cat
-
-
1
expect(Contentful::ContentTypeCache.cache).not_to be_empty
-
end
-
end
-
end
-
-
1
describe ':api_url' do
-
1
it 'is "cdn.contentful.com" [default]' do
-
1
expect(
-
create_client.configuration[:api_url]
-
).to eq 'cdn.contentful.com'
-
end
-
-
1
it 'will be used as base url' do
-
1
expect(
-
create_client(api_url: 'cdn2.contentful.com').base_url
-
).to start_with 'https://cdn2.contentful.com'
-
end
-
end
-
-
1
describe ':api_version' do
-
1
it 'is 1 [default]' do
-
1
expect(
-
create_client.configuration[:api_version]
-
).to eq 1
-
end
-
-
1
it 'will used for the http content type request header' do
-
1
expect(
-
create_client(api_version: 2).request_headers['Content-Type']
-
).to eq 'application/vnd.contentful.delivery.v2+json'
-
end
-
end
-
-
1
describe ':authentication_mechanism' do
-
1
describe ':header [default]' do
-
1
it 'will add the :access_token as authorization bearer token request header' do
-
1
expect(
-
create_client.request_headers['Authorization']
-
).to eq 'Bearer b4c0n73n7fu1'
-
end
-
-
1
it 'will not add :access_token to query' do
-
1
expect(
-
create_client.request_query({})['access_token']
-
).to be_nil
-
end
-
end
-
-
1
describe ':query_string' do
-
1
it 'will add the :access_token to query' do
-
1
expect(
-
create_client(authentication_mechanism: :query_string).
-
request_query({})['access_token']
-
).to eq 'b4c0n73n7fu1'
-
end
-
-
1
it 'will not add the :access_token as authorization bearer token request header' do
-
1
expect(
-
create_client(authentication_mechanism: :query_string).
-
request_headers['Authorization']
-
).to be_nil
-
end
-
end
-
end
-
-
1
describe ':resource_mapping' do
-
1
it 'lets you register your own resource classes for certain response types' do
-
1
class MyBetterAsset < Contentful::Asset
-
1
def https_image_url
-
1
image_url.sub %r{\A//}, 'https://'
-
end
-
end
-
-
1
client = create_client(
-
resource_mapping: {
-
'Asset' => MyBetterAsset,
-
}
-
)
-
-
2
nyancat = vcr('asset') { client.asset 'nyancat' }
-
1
expect(nyancat).to be_a MyBetterAsset
-
1
expect(nyancat.https_image_url).to start_with 'https'
-
end
-
end
-
-
1
describe ':entry_mapping' do
-
1
it 'lets you register your own entry classes for certain entry types' do
-
1
class Cat < Contentful::Entry
-
end
-
-
1
client = create_client(
-
entry_mapping: {
-
'cat' => Cat
-
}
-
)
-
-
2
nyancat = vcr('entry') { client.entry 'nyancat' }
-
2
finn = vcr('human') { client.entry 'finn' }
-
1
expect(nyancat).to be_a Cat
-
1
expect(finn).to be_a Contentful::Entry
-
end
-
end
-
-
1
describe 'X-Contentful-User-Agent headers' do
-
1
it 'default values' do
-
1
expected = [
-
"sdk contentful.rb/#{Contentful::VERSION};",
-
"platform ruby/#{RUBY_VERSION};",
-
]
-
-
1
client = create_client
-
1
expected.each do |h|
-
2
expect(client.contentful_user_agent).to include(h)
-
end
-
-
1
expect(client.contentful_user_agent).to match(/os (Windows|macOS|Linux)(\/.*)?;/i)
-
-
1
['integration', 'app'].each do |h|
-
2
expect(client.contentful_user_agent).not_to include(h)
-
end
-
end
-
-
1
it 'with integration name only' do
-
1
expected = [
-
"sdk contentful.rb/#{Contentful::VERSION};",
-
"platform ruby/#{RUBY_VERSION};",
-
"integration foobar;"
-
]
-
-
1
client = create_client(integration_name: 'foobar')
-
1
expected.each do |h|
-
3
expect(client.contentful_user_agent).to include(h)
-
end
-
-
1
expect(client.contentful_user_agent).to match(/os (Windows|macOS|Linux)(\/.*)?;/i)
-
-
1
['app'].each do |h|
-
1
expect(client.contentful_user_agent).not_to include(h)
-
end
-
end
-
-
1
it 'with integration' do
-
1
expected = [
-
"sdk contentful.rb/#{Contentful::VERSION};",
-
"platform ruby/#{RUBY_VERSION};",
-
"integration foobar/0.1.0;"
-
]
-
-
1
client = create_client(integration_name: 'foobar', integration_version: '0.1.0')
-
1
expected.each do |h|
-
3
expect(client.contentful_user_agent).to include(h)
-
end
-
-
1
expect(client.contentful_user_agent).to match(/os (Windows|macOS|Linux)(\/.*)?;/i)
-
-
1
['app'].each do |h|
-
1
expect(client.contentful_user_agent).not_to include(h)
-
end
-
end
-
-
1
it 'with application name only' do
-
1
expected = [
-
"sdk contentful.rb/#{Contentful::VERSION};",
-
"platform ruby/#{RUBY_VERSION};",
-
"app fooapp;"
-
]
-
-
1
client = create_client(application_name: 'fooapp')
-
1
expected.each do |h|
-
3
expect(client.contentful_user_agent).to include(h)
-
end
-
-
1
expect(client.contentful_user_agent).to match(/os (Windows|macOS|Linux)(\/.*)?;/i)
-
-
1
['integration'].each do |h|
-
1
expect(client.contentful_user_agent).not_to include(h)
-
end
-
end
-
-
1
it 'with application' do
-
1
expected = [
-
"sdk contentful.rb/#{Contentful::VERSION};",
-
"platform ruby/#{RUBY_VERSION};",
-
"app fooapp/1.0.0;"
-
]
-
-
1
client = create_client(application_name: 'fooapp', application_version: '1.0.0')
-
1
expected.each do |h|
-
3
expect(client.contentful_user_agent).to include(h)
-
end
-
-
1
expect(client.contentful_user_agent).to match(/os (Windows|macOS|Linux)(\/.*)?;/i)
-
-
1
['integration'].each do |h|
-
1
expect(client.contentful_user_agent).not_to include(h)
-
end
-
end
-
-
1
it 'with all' do
-
1
expected = [
-
"sdk contentful.rb/#{Contentful::VERSION};",
-
"platform ruby/#{RUBY_VERSION};",
-
"integration foobar/0.1.0;",
-
"app fooapp/1.0.0;"
-
]
-
-
1
client = create_client(
-
integration_name: 'foobar',
-
integration_version: '0.1.0',
-
application_name: 'fooapp',
-
application_version: '1.0.0'
-
)
-
-
1
expected.each do |h|
-
4
expect(client.contentful_user_agent).to include(h)
-
end
-
-
1
expect(client.contentful_user_agent).to match(/os (Windows|macOS|Linux)(\/.*)?;/i)
-
end
-
-
1
it 'when only version numbers, skips header' do
-
1
expected = [
-
"sdk contentful.rb/#{Contentful::VERSION};",
-
"platform ruby/#{RUBY_VERSION};"
-
]
-
-
1
client = create_client(
-
integration_version: '0.1.0',
-
application_version: '1.0.0'
-
)
-
-
1
expected.each do |h|
-
2
expect(client.contentful_user_agent).to include(h)
-
end
-
-
1
expect(client.contentful_user_agent).to match(/os (Windows|macOS|Linux)(\/.*)?;/i)
-
-
1
['integration', 'app'].each do |h|
-
2
expect(client.contentful_user_agent).not_to include(h)
-
end
-
end
-
-
1
it 'headers include X-Contentful-User-Agent' do
-
1
client = create_client
-
1
expect(client.request_headers['X-Contentful-User-Agent']).to eq client.contentful_user_agent
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::ContentType do
-
15
let(:content_type) { vcr('content_type') { create_client.content_type 'cat' } }
-
-
1
describe 'SystemProperties' do
-
1
it 'has a #sys getter returning a hash with symbol keys' do
-
1
expect(content_type.sys).to be_a Hash
-
1
expect(content_type.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it 'has #id' do
-
1
expect(content_type.id).to eq 'cat'
-
end
-
-
1
it 'has #type' do
-
1
expect(content_type.type).to eq 'ContentType'
-
end
-
end
-
-
1
describe 'Properties' do
-
1
it 'has #name' do
-
1
expect(content_type.name).to eq 'Cat'
-
end
-
-
1
it 'has #description' do
-
1
expect(content_type.description).to eq 'Meow.'
-
end
-
-
1
it 'has #fields' do
-
1
expect(content_type.fields).to be_a Array
-
1
expect(content_type.fields.first).to be_a Contentful::Field
-
end
-
-
1
it 'could have #display_field' do
-
1
expect(content_type).to respond_to :display_field
-
end
-
end
-
-
1
describe 'camel case' do
-
1
it 'supports camel case' do
-
1
vcr('content_type') {
-
1
content_type = create_client(use_camel_case: true).content_type 'cat'
-
-
1
expect(content_type.displayField).to eq 'name'
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe 'DeletedAsset' do
-
1
let(:deleted_asset)do
-
4
vcr('sync_deleted_asset')do
-
4
create_client.sync(initial: true, type: 'DeletedAsset').first_page.items[0]
-
end
-
end
-
-
1
describe 'SystemProperties' do
-
1
it 'has a #sys getter returning a hash with symbol keys' do
-
1
expect(deleted_asset.sys).to be_a Hash
-
1
expect(deleted_asset.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it 'has #id' do
-
1
expect(deleted_asset.id).to eq '5c6VY0gWg0gwaIeYkUUiqG'
-
end
-
-
1
it 'has #type' do
-
1
expect(deleted_asset.type).to eq 'DeletedAsset'
-
end
-
-
1
it 'has #deleted_at' do
-
1
expect(deleted_asset.created_at).to be_a DateTime
-
end
-
end
-
-
1
describe 'camel case' do
-
1
it 'supports camel case' do
-
1
vcr('sync_deleted_asset') {
-
1
deleted_asset = create_client(use_camel_case: true).sync(initial: true, type: 'DeletedAsset').first_page.items[0]
-
-
1
expect(deleted_asset.createdAt).to be_a DateTime
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe 'DeletedEntry' do
-
1
let(:deleted_entry)do
-
4
vcr('sync_deleted_entry')do
-
4
create_client.sync(initial: true, type: 'DeletedEntry').first_page.items[0]
-
end
-
end
-
-
1
describe 'SystemProperties' do
-
1
it 'has a #sys getter returning a hash with symbol keys' do
-
1
expect(deleted_entry.sys).to be_a Hash
-
1
expect(deleted_entry.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it 'has #id' do
-
1
expect(deleted_entry.id).to eq 'CVebBDcQsSsu6yKKIayy'
-
end
-
-
1
it 'has #type' do
-
1
expect(deleted_entry.type).to eq 'DeletedEntry'
-
end
-
-
1
it 'has #deleted_at' do
-
1
expect(deleted_entry.created_at).to be_a DateTime
-
end
-
end
-
-
1
describe 'camel case' do
-
1
it 'supports camel case' do
-
1
vcr('sync_deleted_entry') {
-
1
deleted_entry = create_client(use_camel_case: true).sync(initial: true, type: 'DeletedEntry').first_page.items[0]
-
-
1
expect(deleted_entry.createdAt).to be_a DateTime
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Entry do
-
25
let(:entry) { vcr('entry') { create_client.entry 'nyancat' } }
-
-
1
describe 'SystemProperties' do
-
1
it 'has a #sys getter returning a hash with symbol keys' do
-
1
expect(entry.sys).to be_a Hash
-
1
expect(entry.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it 'has #id' do
-
1
expect(entry.id).to eq 'nyancat'
-
end
-
-
1
it 'has #type' do
-
1
expect(entry.type).to eq 'Entry'
-
end
-
-
1
it 'has #space' do
-
1
expect(entry.space).to be_a Contentful::Link
-
end
-
-
1
it 'has #content_type' do
-
1
expect(entry.content_type).to be_a Contentful::Link
-
end
-
-
1
it 'has #created_at' do
-
1
expect(entry.created_at).to be_a DateTime
-
end
-
-
1
it 'has #updated_at' do
-
1
expect(entry.updated_at).to be_a DateTime
-
end
-
-
1
it 'has #revision' do
-
1
expect(entry.revision).to eq 5
-
end
-
end
-
-
1
describe 'Fields' do
-
1
it 'has a #fields getter returning a hash with symbol keys' do
-
1
expect(entry.sys).to be_a Hash
-
1
expect(entry.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it "contains the entry's fields" do
-
1
expect(entry.fields[:color]).to eq 'rainbow'
-
1
expect(entry.fields[:best_friend]).to be_a Contentful::Entry
-
end
-
end
-
-
1
describe 'multiple locales' do
-
1
it 'can handle multiple locales' do
-
1
vcr('entry_locales') {
-
1
nyancat = create_client.entries(locale: "*", 'sys.id' => 'nyancat').items.first
-
1
expect(nyancat.fields('en-US')[:name]).to eq "Nyan Cat"
-
1
expect(nyancat.fields('tlh')[:name]).to eq "Nyan vIghro'"
-
-
-
1
expect(nyancat.fields(:'en-US')[:name]).to eq "Nyan Cat"
-
1
expect(nyancat.fields(:tlh)[:name]).to eq "Nyan vIghro'"
-
}
-
end
-
-
1
describe '#fields_with_locales' do
-
1
it 'can handle entries with just 1 locale' do
-
1
vcr('entry') {
-
1
nyancat = create_client.entry('nyancat')
-
1
expect(nyancat.fields_with_locales[:name].size).to eq(1)
-
1
expect(nyancat.fields_with_locales[:name][:'en-US']).to eq("Nyan Cat")
-
}
-
end
-
-
1
it 'can handle entries with multiple locales' do
-
1
vcr('entry_locales') {
-
1
nyancat = create_client.entries(locale: "*", 'sys.id' => 'nyancat').items.first
-
1
expect(nyancat.fields_with_locales[:name].size).to eq(2)
-
1
expect(nyancat.fields_with_locales[:name][:'en-US']).to eq("Nyan Cat")
-
1
expect(nyancat.fields_with_locales[:name][:tlh]).to eq("Nyan vIghro'")
-
}
-
end
-
-
1
it 'can have references in multiple locales and they are properly solved' do
-
1
vcr('multi_locale_reference') {
-
1
client = create_client(
-
space: '1sjfpsn7l90g',
-
access_token: 'e451a3cdfced9000220be41ed9c899866e8d52aa430eaf7c35b09df8fc6326f9',
-
dynamic_entries: :auto
-
)
-
-
1
entry = client.entries(locale: '*').first
-
-
1
expect(entry.image).to be_a ::Contentful::Asset
-
1
expect(entry.fields('zh')[:image]).to be_a ::Contentful::Asset
-
1
expect(entry.fields('es')[:image]).to be_a ::Contentful::Asset
-
-
1
expect(entry.image.id).not_to eq entry.fields('zh')[:image].id
-
}
-
end
-
-
1
it 'can have references with arrays in multiple locales and have them properly solved' do
-
1
vcr('multi_locale_array_reference') {
-
1
client = create_client(
-
space: 'cma9f9g4dxvs',
-
access_token: '3e4560614990c9ac47343b9eea762bdaaebd845766f619660d7230787fd545e1',
-
dynamic_entries: :auto
-
)
-
-
1
entry = client.entries(content_type: 'test', locale: '*').first
-
-
1
expect(entry.files).to be_a ::Array
-
1
expect(entry.references).to be_a ::Array
-
1
expect(entry.files.first).to be_a ::Contentful::Asset
-
1
expect(entry.references.first.entry?).to be_truthy
-
-
1
expect(entry.fields('zh')[:files]).to be_a ::Array
-
1
expect(entry.fields('zh')[:references]).to be_a ::Array
-
1
expect(entry.fields('zh')[:files].first).to be_a ::Contentful::Asset
-
1
expect(entry.fields('zh')[:references].first.entry?).to be_truthy
-
-
1
expect(entry.files.first.id).not_to eq entry.fields('zh')[:files].first.id
-
}
-
end
-
end
-
end
-
-
1
it '#raw' do
-
1
vcr('entry/raw') {
-
1
nyancat = create_client.entry('nyancat')
-
1
expect(nyancat.raw).to eq(create_client(raw_mode: true).entry('nyancat').object['items'].first)
-
}
-
end
-
-
1
describe 'can be marshalled' do
-
1
def test_dump(nyancat)
-
2
dump = Marshal.dump(nyancat)
-
2
new_cat = Marshal.load(dump)
-
-
# Attributes
-
2
expect(new_cat).to be_a Contentful::Entry
-
2
expect(new_cat.name).to eq "Nyan Cat"
-
2
expect(new_cat.lives).to eq 1337
-
-
# Single linked objects
-
2
expect(new_cat.best_friend).to be_a Contentful::Entry
-
2
expect(new_cat.best_friend.name).to eq "Happy Cat"
-
-
# Array of linked objects
-
2
expect(new_cat.cat_pack.count).to eq 2
-
2
expect(new_cat.cat_pack[0].name).to eq "Happy Cat"
-
2
expect(new_cat.cat_pack[1].name).to eq "Worried Cat"
-
-
# Nested Links
-
2
expect(new_cat.best_friend.best_friend).to be_a Contentful::Entry
-
2
expect(new_cat.best_friend.best_friend.name).to eq "Nyan Cat"
-
-
# Asset
-
2
expect(new_cat.image.file.url).to eq "//images.contentful.com/cfexampleapi/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png"
-
end
-
-
1
it 'marshals properly' do
-
1
vcr('entry/marshall') {
-
1
nyancat = create_client(gzip_encoded: false, max_include_resolution_depth: 2).entries(include: 2, 'sys.id' => 'nyancat').first
-
1
test_dump(nyancat)
-
}
-
end
-
-
1
it 'can remarshall an unmarshalled object' do
-
1
vcr('entry/marshall') {
-
1
nyancat = create_client(max_include_resolution_depth: 2).entries(include: 2, 'sys.id' => 'nyancat').first
-
-
# The double load/dump is on purpose
-
1
test_dump(Marshal.load(Marshal.dump(nyancat)))
-
}
-
end
-
-
1
it 'can properly marshal multiple level nested resources - #138' do
-
1
vcr('entry/marshal_138') {
-
1
parent = create_client(
-
space: 'j8tb59fszch7',
-
access_token: '5f711401f965951eb724ac72ac905e13d892294ba209268f13a9b32e896c8694',
-
dynamic_entries: :auto,
-
max_include_resolution_depth: 5
-
).entry('5aV3O0l5jU0cwQ2OkyYsyU')
-
-
1
rehydrated = Marshal.load(Marshal.dump(parent))
-
-
1
expect(rehydrated.childs.first.image1.url).to eq '//images.contentful.com/j8tb59fszch7/7FjliblAmAoGMwU62MeQ6k/62509df90ef4bed38c0701bb9aa8c74c/Funny-Cat-Pictures-with-Captions-25.jpg'
-
1
expect(rehydrated.childs.first.image2.url).to eq '//images.contentful.com/j8tb59fszch7/1pbGuWZ27O6GMO0OGemgcA/a4185036a3640ad4491f38d8926003ab/Funny-Cat-Pictures-with-Captions-1.jpg'
-
1
expect(rehydrated.childs.last.image1.url).to eq '//images.contentful.com/j8tb59fszch7/4SXVTr0KEUyWiMMCOaUeUU/c9fa2246d5529a9c8e1ec6f5387dc4f6/e0194eca1c8135636ce0e014341548c3.jpg'
-
1
expect(rehydrated.childs.last.image2.url).to eq '//images.contentful.com/j8tb59fszch7/1NU1YcNQJGIA22gAKmKqWo/56fa672bb17a7b7ae2773d08e101d059/57ee64921c25faa649fc79288197c313.jpg'
-
}
-
end
-
end
-
-
1
describe 'incoming links' do
-
4
let(:client) { create_client }
-
-
1
it 'will fetch entries referencing the entry using a query' do
-
1
vcr('entry/search_link_to_entry') {
-
1
entries = client.entries(links_to_entry: 'nyancat')
-
1
expect(entries).not_to be_empty
-
1
expect(entries.count).to eq 1
-
1
expect(entries.first.id).to eq 'happycat'
-
}
-
end
-
-
1
it 'will fetch entries referencing the entry using instance method' do
-
1
vcr('entry/search_link_to_entry') {
-
1
entries = entry.incoming_references client
-
1
expect(entries).not_to be_empty
-
1
expect(entries.count).to eq 1
-
1
expect(entries.first.id).to eq 'happycat'
-
}
-
end
-
-
1
it 'will fetch entries referencing the entry using instance method + query' do
-
1
vcr('entry/search_link_to_entry_with_custom_query') {
-
1
entries = entry.incoming_references(client, { content_type: 'cat', select: ['fields.name'] })
-
1
expect(entries).not_to be_empty
-
1
expect(entries.count).to eq 1
-
1
expect(entries.first.id).to eq 'happycat'
-
1
expect(entries.first.fields.keys).to eq([:name])
-
}
-
end
-
-
end
-
-
1
describe 'select operator' do
-
6
let(:client) { create_client }
-
-
1
context 'with sys sent' do
-
1
it 'properly creates an entry' do
-
1
vcr('entry/select_only_sys') {
-
1
entry = client.entries(select: ['sys'], 'sys.id' => 'nyancat').first
-
1
expect(entry.fields).to be_empty
-
1
expect(entry.entry?).to be_truthy
-
}
-
end
-
-
1
describe 'can contain only one field' do
-
1
context 'with content_type sent' do
-
1
it 'will properly create the entry with one field' do
-
1
vcr('entry/select_one_field_proper') {
-
1
entry = client.entries(content_type: 'cat', select: ['sys', 'fields.name'], 'sys.id' => 'nyancat').first
-
1
expect(entry.fields).not_to be_empty
-
1
expect(entry.entry?).to be_truthy
-
1
expect(entry.fields[:name]).to eq 'Nyan Cat'
-
1
expect(entry.fields).to eq({name: 'Nyan Cat'})
-
}
-
end
-
end
-
-
1
context 'without content_type sent' do
-
1
it 'will raise an error' do
-
1
vcr('entry/select_one_field') {
-
2
expect { client.entries(select: ['sys', 'fields.name'], 'sys.id' => 'nyancat') }.to raise_error Contentful::BadRequest
-
}
-
end
-
end
-
end
-
end
-
-
1
context 'without sys sent' do
-
1
it 'will enforce sys anyway' do
-
1
vcr('entry/select_no_sys') {
-
1
entry = client.entries(select: ['fields'], 'sys.id' => 'nyancat').first
-
-
1
expect(entry.id).to eq 'nyancat'
-
1
expect(entry.sys).not_to be_empty
-
}
-
end
-
-
1
it 'works with empty array as well, as sys is enforced' do
-
1
vcr('entry/select_empty_array') {
-
1
entry = client.entries(select: [], 'sys.id' => 'nyancat').first
-
-
1
expect(entry.id).to eq 'nyancat'
-
1
expect(entry.sys).not_to be_empty
-
}
-
end
-
end
-
end
-
-
1
describe 'reuse objects' do
-
1
it 'should handle recursion as well as not reusing' do
-
1
vcr('entry/include_resolution') {
-
1
entry = create_client(reuse_objects: true).entry('nyancat', include: 2)
-
-
1
expect(entry.best_friend.name).to eq 'Happy Cat'
-
1
expect(entry
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend.name).to eq 'Nyan Cat'
-
}
-
end
-
1
it 'should use the same object for the same entry' do
-
1
vcr('entry/include_resolution') {
-
1
entry = create_client(reuse_entries: true).entry('nyancat', include: 2)
-
-
1
expect(entry.best_friend.name).to eq 'Happy Cat'
-
1
expect(entry.best_friend.best_friend).to be(entry)
-
}
-
end
-
1
it 'works on nested structures with unique objects' do
-
1
vcr('entry/include_resolution_uniques') {
-
1
entry = create_client(
-
space: 'v7cxgyxt0w5x',
-
access_token: '96e5d256e9a5349ce30e84356597e409f8f1bb485cb4719285b555e0f78aa27e',
-
reuse_entries: true
-
).entry('1nLXjjWvk4MEeWeQCWmymc', include: 10)
-
-
1
expect(entry.title).to eq '1'
-
1
expect(entry
-
.child.child
-
.child.child
-
.child.child
-
.child.child
-
.child.title).to eq '10'
-
1
expect(entry
-
.child.child
-
.child.child
-
.child.child
-
.child.child
-
.child.child.title).to eq '1'
-
1
expect(entry
-
.child.child.child.child
-
.child.child.child.child
-
.child.child.child.child
-
.child.child.child.child
-
.child.child.child.child.title).to eq '1'
-
}
-
end
-
end
-
-
1
describe 'include resolution' do
-
1
it 'should not reuse objects by default' do
-
1
vcr('entry/include_resolution') {
-
1
entry = create_client.entry('nyancat', include: 2)
-
-
1
expect(entry.best_friend.name).to eq 'Happy Cat'
-
1
expect(entry.best_friend.best_friend).not_to be(entry)
-
}
-
end
-
1
it 'defaults to 20 depth' do
-
1
vcr('entry/include_resolution') {
-
1
entry = create_client.entry('nyancat', include: 2)
-
-
1
expect(entry.best_friend.name).to eq 'Happy Cat'
-
1
expect(entry
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend.name).to eq 'Nyan Cat'
-
-
1
expect(entry
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend.best_friend
-
.best_friend).to be_a ::Contentful::Link
-
}
-
end
-
-
1
it 'can be configured arbitrarily' do
-
1
vcr('entry/include_resolution') {
-
1
entry = create_client(max_include_resolution_depth: 3).entry('nyancat', include: 2)
-
-
1
expect(entry.best_friend.name).to eq 'Happy Cat'
-
1
expect(entry
-
.best_friend.best_friend
-
.best_friend.name).to eq 'Happy Cat'
-
1
expect(entry
-
.best_friend.best_friend
-
.best_friend.best_friend).to be_a ::Contentful::Link
-
}
-
end
-
1
it 'works on nested structures with unique objects' do
-
1
vcr('entry/include_resolution_uniques') {
-
1
entry = create_client(
-
space: 'v7cxgyxt0w5x',
-
access_token: '96e5d256e9a5349ce30e84356597e409f8f1bb485cb4719285b555e0f78aa27e',
-
).entry('1nLXjjWvk4MEeWeQCWmymc', include: 10)
-
-
1
expect(entry.title).to eq '1'
-
1
expect(entry
-
.child.child
-
.child.child
-
.child.child
-
.child.child
-
.child.title).to eq '10'
-
1
expect(entry
-
.child.child
-
.child.child
-
.child.child
-
.child.child
-
.child.child.title).to eq '1'
-
1
expect(entry
-
.child.child.child.child
-
.child.child.child.child
-
.child.child.child.child
-
.child.child.child.child
-
.child.child.child.child.title).to eq '1'
-
}
-
end
-
end
-
-
1
describe 'issues' do
-
1
it 'Symbol/Text field with null values should be serialized as nil - #117' do
-
1
vcr('entries/issue_117') {
-
1
client = create_client(space: '8jbbayggj9gj', access_token: '4ce0108f04e55c76476ba84ab0e6149734db73d67cd1b429323ef67f00977e07')
-
1
entry = client.entries.first
-
-
1
expect(entry.nil).to be_nil
-
1
expect(entry.nil).not_to eq ''
-
}
-
end
-
-
1
describe 'JSON Fields should not be treated as locale data - #96' do
-
1
before do
-
2
vcr('entry/json_objects_client') {
-
2
@client = create_client(
-
space: 'h425t6gef30p',
-
access_token: '278f7aa72f2eb90c0e002d60f85bf2144c925acd2d37dd990d3ca274f25076cf',
-
dynamic_entries: :auto
-
)
-
-
}
-
2
vcr('entry/json_objects') {
-
2
@entry = @client.entries.first
-
}
-
end
-
-
1
it 'only has default locale' do
-
1
expect(@entry.locales).to eq ['en-US']
-
end
-
-
1
it 'can obtain all values properly' do
-
1
expect(@entry.name).to eq('Test')
-
1
expect(@entry.object_test).to eq({
-
null: nil,
-
text: 'some text',
-
array: [1, 2, 3],
-
number: 123,
-
boolean: true,
-
object: {
-
null: nil,
-
text: 'bar',
-
array: [1, 2, 3],
-
number: 123,
-
boolean: false,
-
object: {foo: 'bar'}
-
}
-
})
-
end
-
end
-
-
1
it 'Number (Integer and Decimal) values get properly serialized - #125' do
-
1
vcr('entries/issue_125') {
-
1
client = create_client(space: 'zui87wsu8q80', access_token: '64ff902c58cd14ea063d3ded810d1111a0266537e9aba283bad3319b1762c302', dynamic_entries: :auto)
-
1
entry = client.entries.first
-
-
1
expect(entry.integer).to eq 123
-
1
expect(entry.decimal).to eq 12.3
-
}
-
end
-
-
1
it 'unresolvable entries get filtered from results' do
-
1
vcr('entries/unresolvable_filter') {
-
1
client = create_client(space: '011npgaszg5o', access_token: '42c9d93410a7319e9a735671fc1e415348f65e94a99fc768b70a7c649859d4fd', dynamic_entries: :auto)
-
1
entry = client.entry('1HR1QvURo4MoSqO0eqmUeO')
-
-
1
expect(entry.modules.size).to eq 2
-
}
-
end
-
-
1
it 'unresolvable entries get filtered from results in deeply nested objects - #177' do
-
1
vcr('entries/unresolvable_filter_deeply_nested') {
-
1
client = create_client(space: 'z471hdso7l1a', access_token: '8a0e09fe71f1cb41e8788ace86a8c8d9d084599fe43a40070f232045014d2585', dynamic_entries: :auto)
-
1
entry = client.entry('1hb8sipClkQ8ggeGaeSQWm', include: 3)
-
1
expect(entry.should_published.first.should_unpublished.size).to eq 0
-
}
-
end
-
end
-
-
1
describe 'camel case' do
-
1
it 'supports camel case' do
-
1
vcr('entry') {
-
1
entry = create_client(use_camel_case: true).entry 'nyancat'
-
-
1
expect(entry.bestFriend.name).to eq 'Happy Cat'
-
1
expect(entry.createdAt).to be_a DateTime
-
}
-
end
-
end
-
-
1
describe 'structured text support' do
-
1
it 'properly serializes and resolves includes' do
-
1
vcr('entries/structured_text') {
-
1
entry = create_client(
-
space: 'jd7yc4wnatx3',
-
access_token: '6256b8ef7d66805ca41f2728271daf27e8fa6055873b802a813941a0fe696248',
-
raise_errors: true,
-
dynamic_entries: :auto
-
).entry('4BupPSmi4M02m0U48AQCSM')
-
-
1
expected_entry_occurrances = 2
-
1
embedded_entry_index = 1
-
1
entry.body['content'].each do |content|
-
16
if content['nodeType'] == 'embedded-entry-block'
-
2
expect(content['data']).to be_a Contentful::Entry
-
2
expect(content['data'].body).to eq "Embedded #{embedded_entry_index}"
-
2
expected_entry_occurrances -= 1
-
2
embedded_entry_index += 1
-
end
-
end
-
-
1
expect(expected_entry_occurrances).to eq 0
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Error do
-
3
let(:r) { Contentful::Response.new raw_fixture('not_found', 404) }
-
-
1
describe '#response' do
-
1
it 'returns the response the error has been initialized with' do
-
1
expect(Contentful::Error.new(r).response).to be r
-
end
-
end
-
-
1
describe '#message' do
-
1
it 'returns the message found in the response json' do
-
1
message = "HTTP status code: 404\n"\
-
"Message: The resource could not be found.\n"\
-
"Details: {\"type\"=>\"Entry\", \"space\"=>\"cfexampleapi\", \"id\"=>\"not found\"}\n"\
-
"Request ID: 85f-351076632"
-
1
expect(Contentful::Error.new(r).message).not_to be_nil
-
1
expect(Contentful::Error.new(r).message).to eq message
-
end
-
-
1
describe 'message types' do
-
1
describe 'default messages' do
-
1
it '400' do
-
1
response = Contentful::Response.new raw_fixture('default_400', 400)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 400\n"\
-
"Message: The request was malformed or missing a required parameter.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it '401' do
-
1
response = Contentful::Response.new raw_fixture('default_401', 401)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 401\n"\
-
"Message: The authorization token was invalid.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it '403' do
-
1
response = Contentful::Response.new raw_fixture('default_403', 403)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 403\n"\
-
"Message: The specified token does not have access to the requested resource.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it '404' do
-
1
response = Contentful::Response.new raw_fixture('default_404', 404)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 404\n"\
-
"Message: The requested resource or endpoint could not be found.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it '429' do
-
1
response = Contentful::Response.new raw_fixture('default_429', 429)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 429\n"\
-
"Message: Rate limit exceeded. Too many requests.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it '500' do
-
1
response = Contentful::Response.new raw_fixture('default_500', 500)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 500\n"\
-
"Message: Internal server error.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it '502' do
-
1
response = Contentful::Response.new raw_fixture('default_502', 502)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 502\n"\
-
"Message: The requested space is hibernated.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it '503' do
-
1
response = Contentful::Response.new raw_fixture('default_503', 503)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 503\n"\
-
"Message: The request was malformed or missing a required parameter.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
end
-
-
1
describe 'special cases' do
-
1
describe '400' do
-
1
it 'details is a string' do
-
1
response = Contentful::Response.new raw_fixture('400_details_string', 400)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 400\n"\
-
"Message: The request was malformed or missing a required parameter.\n"\
-
"Details: some error\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it 'details is an object, internal errors are strings' do
-
1
response = Contentful::Response.new raw_fixture('400_details_errors_string', 400)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 400\n"\
-
"Message: The request was malformed or missing a required parameter.\n"\
-
"Details: some error\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it 'details is an object, internal errors are objects which have details' do
-
1
response = Contentful::Response.new raw_fixture('400_details_errors_object', 400)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 400\n"\
-
"Message: The request was malformed or missing a required parameter.\n"\
-
"Details: some error\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
end
-
-
1
describe '403' do
-
1
it 'has an array of reasons' do
-
1
response = Contentful::Response.new raw_fixture('403_reasons', 403)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 403\n"\
-
"Message: The specified token does not have access to the requested resource.\n"\
-
"Details: \n\tReasons:\n"\
-
"\t\tfoo\n"\
-
"\t\tbar\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
end
-
-
1
describe '404' do
-
1
it 'details is a string' do
-
1
response = Contentful::Response.new raw_fixture('404_details_string', 404)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 404\n"\
-
"Message: The requested resource or endpoint could not be found.\n"\
-
"Details: The resource could not be found\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
describe 'has a type' do
-
1
it 'type is on the top level' do
-
1
response = Contentful::Response.new raw_fixture('404_type', 404)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 404\n"\
-
"Message: The requested resource or endpoint could not be found.\n"\
-
"Details: The requested Asset could not be found.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it 'type is not on the top level' do
-
1
response = Contentful::Response.new raw_fixture('404_sys_type', 404)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 404\n"\
-
"Message: The requested resource or endpoint could not be found.\n"\
-
"Details: The requested Space could not be found.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
end
-
-
1
it 'can specify the resource id' do
-
1
response = Contentful::Response.new raw_fixture('404_id', 404)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 404\n"\
-
"Message: The requested resource or endpoint could not be found.\n"\
-
"Details: The requested Asset could not be found. ID: foobar.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
end
-
-
1
describe '429' do
-
1
it 'can show the time until reset' do
-
1
response = Contentful::Response.new raw_fixture('default_429', 429, false, {'x-contentful-ratelimit-reset' => 60})
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 429\n"\
-
"Message: Rate limit exceeded. Too many requests.\n"\
-
"Request ID: 85f-351076632\n"\
-
"Time until reset (seconds): 60"
-
1
expect(error.message).to eq message
-
end
-
end
-
end
-
-
1
describe 'generic error' do
-
1
it 'with everything' do
-
1
response = Contentful::Response.new raw_fixture('other_error', 512)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 512\n"\
-
"Message: Some error occurred.\n"\
-
"Details: some text\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it 'no details' do
-
1
response = Contentful::Response.new raw_fixture('other_error_no_details', 512)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 512\n"\
-
"Message: Some error occurred.\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it 'no request id' do
-
1
response = Contentful::Response.new raw_fixture('other_error_no_request_id', 512)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 512\n"\
-
"Message: Some error occurred.\n"\
-
"Details: some text"
-
1
expect(error.message).to eq message
-
end
-
-
1
it 'no message' do
-
1
response = Contentful::Response.new raw_fixture('other_error_no_message', 512)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 512\n"\
-
"Message: The following error was received: {\n"\
-
" \"sys\": {\n"\
-
" \"type\": \"Error\",\n"\
-
" \"id\": \"SomeError\"\n"\
-
" },\n"\
-
" \"details\": \"some text\",\n"\
-
" \"requestId\": \"85f-351076632\"\n"\
-
"}\n"\
-
"\n"\
-
"Details: some text\n"\
-
"Request ID: 85f-351076632"
-
1
expect(error.message).to eq message
-
end
-
-
1
it 'nothing' do
-
1
response = Contentful::Response.new raw_fixture('other_error_nothing', 512)
-
1
error = Contentful::Error[response.raw.status].new(response)
-
-
1
message = "HTTP status code: 512\n"\
-
"Message: The following error was received: {\n"\
-
" \"sys\": {\n"\
-
" \"type\": \"Error\",\n"\
-
" \"id\": \"SomeError\"\n"\
-
" }\n"\
-
"}\n"
-
1
expect(error.message).to eq message
-
end
-
end
-
end
-
end
-
-
1
describe Contentful::UnparsableJson do
-
1
describe '#message' do
-
1
it 'returns the json parser\'s message' do
-
1
uj = Contentful::Response.new raw_fixture('unparsable')
-
1
expect(Contentful::UnparsableJson.new(uj).message).to \
-
include 'unexpected token'
-
end
-
end
-
end
-
-
1
describe '.[]' do
-
1
it 'returns BadRequest error class for 400' do
-
1
expect(Contentful::Error[400]).to eq Contentful::BadRequest
-
end
-
-
1
it 'returns Unauthorized error class for 401' do
-
1
expect(Contentful::Error[401]).to eq Contentful::Unauthorized
-
end
-
-
1
it 'returns AccessDenied error class for 403' do
-
1
expect(Contentful::Error[403]).to eq Contentful::AccessDenied
-
end
-
-
1
it 'returns NotFound error class for 404' do
-
1
expect(Contentful::Error[404]).to eq Contentful::NotFound
-
end
-
-
1
it 'returns ServerError error class for 500' do
-
1
expect(Contentful::Error[500]).to eq Contentful::ServerError
-
end
-
-
1
it 'returns ServiceUnavailable error class for 503' do
-
1
expect(Contentful::Error[503]).to eq Contentful::ServiceUnavailable
-
end
-
-
1
it 'returns generic error class for any other value' do
-
1
expect(Contentful::Error[nil]).to eq Contentful::Error
-
1
expect(Contentful::Error[200]).to eq Contentful::Error
-
end
-
end
-
-
end
-
1
require 'spec_helper'
-
-
1
class NonCachingClient < Contentful::Client
-
1
def request_headers
-
8
headers = super
-
8
headers['Cf-No-Cache'] = 'foobar'
-
8
headers
-
end
-
end
-
-
1
class RetryLoggerMock < Logger
-
1
attr_reader :retry_attempts
-
-
1
def initialize(*)
-
1
super
-
1
@retry_attempts = 0
-
end
-
-
1
def info(message)
-
5
super
-
5
@retry_attempts += 1 if message.include?('Contentful API Rate Limit Hit! Retrying')
-
end
-
end
-
-
1
describe 'Error Requests' do
-
1
it 'will return 404 (Unauthorized) if resource not found' do
-
1
expect_vcr('not found')do
-
1
create_client.content_type 'not found'
-
end.to raise_error(Contentful::NotFound)
-
end
-
-
1
it 'will return 400 (BadRequest) if invalid parameters have been passed' do
-
1
expect_vcr('bad request')do
-
1
create_client.entries(some: 'parameter')
-
end.to raise_error(Contentful::BadRequest)
-
end
-
-
1
it 'will return 403 (AccessDenied) if ...' do
-
1
skip
-
end
-
-
1
it 'will return 401 (Unauthorized) if wrong credentials are given' do
-
1
client = Contentful::Client.new(space: 'wrong', access_token: 'credentials')
-
-
1
expect_vcr('unauthorized'){
-
1
client.entry('nyancat')
-
}.to raise_error(Contentful::Unauthorized)
-
end
-
-
1
it 'will return 500 (ServerError) if ...' do
-
1
skip
-
end
-
-
1
it 'will return a 429 if the ratelimit is reached and is not set to retry' do
-
1
client = Contentful::Client.new(space: 'wrong', access_token: 'credentials', max_rate_limit_retries: 0)
-
1
expect_vcr('ratelimit') {
-
1
client.entry('nyancat')
-
}.to raise_error(Contentful::RateLimitExceeded)
-
end
-
-
1
it 'will retry on 429 by default' do
-
1
logger = RetryLoggerMock.new(STDOUT)
-
1
client = NonCachingClient.new(
-
api_url: 'cdnorigin.flinkly.com',
-
space: '164vhtp008kz',
-
access_token: '7699b6c6f6cee9b6abaa216c71fbcb3eee56cb6f082f57b5e21b2b50f86bdea0',
-
raise_errors: true,
-
logger: logger
-
)
-
-
1
vcr('ratelimit_retry') {
-
1
3.times {
-
3
client.assets
-
}
-
}
-
-
1
expect(logger.retry_attempts).to eq 1
-
end
-
-
1
it 'will return 503 (ServiceUnavailable) when the service is unavailable' do
-
1
client = Contentful::Client.new(space: 'wrong', access_token: 'credentials')
-
-
1
expect_vcr('unavailable'){
-
1
client.entry('nyancat')
-
}.to raise_error(Contentful::ServiceUnavailable)
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Field do
-
13
let(:field) { vcr('field') { create_client.content_type('cat').fields.first } }
-
3
let(:linkField) { vcr('linkField') {
-
18
create_client.content_type('cat').fields.select { |f| f.id == 'image' }.first
-
} }
-
3
let(:arrayField) { vcr('arrayField') {
-
Contentful::Client.new(
-
space: 'wl1z0pal05vy',
-
access_token: '9b76e1bbc29eb513611a66b9fc5fb7acd8d95e83b0f7d6bacfe7ec926c819806'
-
26
).content_type('2PqfXUJwE8qSYKuM0U6w8M').fields.select { |f| f.id == 'categories' }.first
-
} }
-
-
1
describe 'Properties' do
-
1
it 'has #id' do
-
1
expect(field.id).to eq 'name'
-
end
-
-
1
it 'has #name' do
-
1
expect(field.name).to eq 'Name'
-
end
-
-
1
it 'has #type' do
-
1
expect(field.type).to eq 'Text'
-
end
-
-
1
it 'could have #items' do
-
1
expect(field).to respond_to :items
-
end
-
-
1
it 'has #required' do
-
1
expect(field.required).to be_truthy
-
end
-
-
1
it 'has #localized' do
-
1
expect(field.required).to be_truthy
-
end
-
end
-
-
1
describe 'Link field properties' do
-
1
it 'has #type' do
-
1
expect(linkField.type).to eq 'Link'
-
end
-
-
1
it 'has #linkType' do
-
1
expect(linkField.link_type).to eq 'Asset'
-
end
-
end
-
-
1
describe 'Array field properties' do
-
1
it 'has #type' do
-
1
expect(arrayField.type).to eq 'Array'
-
end
-
-
1
it 'has #items' do
-
1
expect(arrayField.items.type).to eq 'Link'
-
1
expect(arrayField.items.link_type).to eq 'Entry'
-
end
-
end
-
-
1
describe 'issues' do
-
1
describe 'json field' do
-
1
it 'can coerce properly when top level is not object' do
-
1
coercion = Contentful::ObjectCoercion.new([{foo: 123}])
-
1
expect(coercion.coerce).to eq [{foo: 123}]
-
-
1
coercion = Contentful::ObjectCoercion.new('foobar')
-
1
expect(coercion.coerce).to eq 'foobar'
-
-
1
coercion = Contentful::ObjectCoercion.new(true)
-
1
expect(coercion.coerce).to eq true
-
-
1
coercion = Contentful::ObjectCoercion.new(123)
-
1
expect(coercion.coerce).to eq 123
-
-
1
coercion = Contentful::ObjectCoercion.new({foo: 123})
-
1
expect(coercion.coerce).to eq(foo: 123)
-
end
-
end
-
-
1
describe 'datetime field' do
-
1
it 'can coerce properly when value is nil' do
-
1
coercion = Contentful::DateCoercion.new(nil)
-
1
expect(coercion.coerce).to eq(nil)
-
end
-
-
1
it 'can coerce properly when value is already datetime' do
-
1
value = DateTime.new
-
1
coercion = Contentful::DateCoercion.new(value)
-
1
expect(coercion.coerce).to eq value
-
end
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::File do
-
9
let(:file) { vcr('asset') { create_client.asset('nyancat').file } }
-
-
1
describe 'Properties' do
-
1
it 'has #file_name' do
-
1
expect(file.file_name).to eq 'Nyan_cat_250px_frame.png'
-
end
-
-
1
it 'has #content_type' do
-
1
expect(file.content_type).to eq 'image/png'
-
end
-
-
1
it 'has #url' do
-
1
expect(file.url).to eq '//images.contentful.com/cfexampleapi/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png'
-
end
-
-
1
it 'has #details' do
-
1
expect(file.details).to be_instance_of Hash
-
end
-
end
-
-
1
describe 'camel case' do
-
1
it 'supports camel case' do
-
1
vcr('asset') {
-
1
file = create_client(use_camel_case: true).asset('nyancat').file
-
1
expect(file.contentType).to eq 'image/png'
-
1
expect(file.fileName).to eq 'Nyan_cat_250px_frame.png'
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Link do
-
3
let(:client) { create_client }
-
13
let(:entry) { vcr('entry') { create_client.entry('nyancat') } }
-
6
let(:link) { entry.space }
-
2
let(:content_type_link) { entry.content_type }
-
-
1
describe 'SystemProperties' do
-
1
it 'has a #sys getter returning a hash with symbol keys' do
-
1
expect(link.sys).to be_a Hash
-
1
expect(link.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it 'has #id' do
-
1
expect(link.id).to eq 'cfexampleapi'
-
end
-
-
1
it 'has #type' do
-
1
expect(link.type).to eq 'Link'
-
end
-
-
1
it 'has #link_type' do
-
1
expect(link.link_type).to eq 'Space'
-
end
-
end
-
-
1
describe '#resolve' do
-
1
it 'queries the api for the resource' do
-
1
vcr('space')do
-
1
expect(link.resolve(client)).to be_a Contentful::Space
-
end
-
end
-
-
1
it 'queries the api for the resource (different link object)' do
-
1
vcr('content_type')do
-
1
expect(content_type_link.resolve(client)).to be_a Contentful::ContentType
-
end
-
end
-
end
-
-
1
describe 'camel case' do
-
1
it 'supports camel case' do
-
1
vcr('entry') {
-
1
space_link = create_client(use_camel_case: true).entry('nyancat').space
-
1
expect(space_link.linkType).to eq 'Space'
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Locale do
-
7
let(:locale) { vcr('locale') { create_client.space.locales.first } }
-
-
1
describe 'Properties' do
-
1
it 'has #code' do
-
1
expect(locale.code).to eq 'en-US'
-
end
-
-
1
it 'has #name' do
-
1
expect(locale.name).to eq 'English'
-
end
-
-
1
it 'has #default' do
-
1
expect(locale.default).to eq true
-
end
-
end
-
-
1
describe 'locales endpoint' do
-
1
it 'locales can be fetched from environments' do
-
1
vcr('locale_from_environment') {
-
1
client = create_client(
-
space: 'facgnwwgj5fe',
-
access_token: '<ACCESS_TOKEN>',
-
environment: 'testing'
-
)
-
-
1
locales = client.locales
-
-
1
expect(locales).to be_a ::Contentful::Array
-
1
expect(locales.first).to be_a ::Contentful::Locale
-
1
expect(locales.first.code).to eq 'en-US'
-
}
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Location do
-
1
let(:location)do
-
2
vcr('location')do
-
Contentful::Client.new(
-
space: 'lzjz8hygvfgu',
-
access_token: '0c6ef483524b5e46b3bafda1bf355f38f5f40b4830f7599f790a410860c7c271',
-
dynamic_entries: :auto,
-
2
).entry('3f6fq5ylFCi4kIYAQKsAYG').location
-
end
-
end
-
-
1
describe 'Properties' do
-
1
it 'has #lat' do
-
1
expect(location.lat).to be_a Float
-
1
expect(location.lat.to_i).to eq 36
-
end
-
-
1
it 'has #lon' do
-
1
expect(location.lon).to be_a Float
-
1
expect(location.lon.to_i).to eq(-94)
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Request do
-
1
describe '#get' do
-
1
it 'calls client' do
-
1
client = create_client
-
1
request = Contentful::Request.new(client, '/content_types', nil, 'nyancat')
-
-
1
expect(client).to receive(:get).with(request)
-
-
1
request.get
-
end
-
end
-
-
1
describe '#query' do
-
1
it 'converts arrays given in query to comma strings' do
-
1
client = create_client
-
1
request = Contentful::Request.new(client, '/entries', 'fields.likes[in]' => %w(jake finn))
-
1
expect(request.query[:'fields.likes[in]']).to eq 'jake,finn'
-
end
-
end
-
-
1
context '[single resource]' do
-
1
let(:request)do
-
2
Contentful::Request.new(create_client, '/content_types', nil, 'nyancat')
-
end
-
-
1
describe '#url' do
-
1
it 'contais endpoint' do
-
1
expect(request.url).to include 'content_types'
-
end
-
-
1
it 'contains id' do
-
1
expect(request.url).to include 'nyancat'
-
end
-
end
-
end
-
-
1
context '[multi resource]' do
-
1
let(:request)do
-
2
Contentful::Request.new(create_client, '/content_types', 'something' => 'requested')
-
end
-
-
1
describe '#query' do
-
1
it 'contains query' do
-
1
expect(request.query).not_to be_empty
-
1
expect(request.query[:something]).to eq 'requested'
-
end
-
end
-
-
1
describe '#url' do
-
1
it 'contais endpoint' do
-
1
expect(request.url).to include 'content_types'
-
end
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe 'Resource Building Examples' do
-
1
it 'can deal with arrays' do
-
1
request = Contentful::Request.new(nil, 'entries')
-
1
response = Contentful::Response.new(raw_fixture('link_array'), request)
-
1
resource = Contentful::ResourceBuilder.new(response.object).run
-
-
1
expect(resource.fields[:links]).to be_a Array
-
1
expect(resource.fields[:links].first).to be_a Contentful::Link
-
end
-
-
1
it 'replaces links with included versions if present' do
-
1
request = Contentful::Request.new(nil, 'entries')
-
1
response = Contentful::Response.new(raw_fixture('includes'), request)
-
1
resource = Contentful::ResourceBuilder.new(response.object).run.first
-
-
1
expect(resource.fields[:links]).to be_a Array
-
1
expect(resource.fields[:links].first).to be_a Contentful::Entry
-
end
-
-
1
it 'can also reference itself' do
-
1
request = Contentful::Request.new(nil, 'entries')
-
1
response = Contentful::Response.new(raw_fixture('self_link'), request)
-
1
resource = Contentful::ResourceBuilder.new(response.object).run.first
-
-
1
other_resource = resource.fields[:e]
-
1
expect(other_resource).to be_a Contentful::Entry
-
1
expect(other_resource.fields[:e]).to eq resource
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Response do
-
5
let(:successful_response) { Contentful::Response.new raw_fixture('nyancat'), Contentful::Request.new(nil, '/entries', nil, 'nyancat') }
-
3
let(:error_response) { Contentful::Response.new raw_fixture('not_found', 404) }
-
3
let(:unparsable_response) { Contentful::Response.new raw_fixture('unparsable') }
-
-
1
describe '#raw' do
-
1
it 'returns the raw response it has been initalized with' do
-
1
expect(successful_response.raw.to_s).to eql raw_fixture('nyancat').to_s
-
end
-
end
-
-
1
describe '#object' do
-
1
it "returns the repsonse's parsed json" do
-
1
expect(successful_response.object).to eq json_fixture('nyancat')
-
end
-
end
-
-
1
describe '#request' do
-
1
it 'returns the request the response has been initalized with' do
-
1
expect(successful_response.request).to be_a Contentful::Request
-
end
-
end
-
-
1
describe '#status' do
-
1
it 'returns :ok for normal responses' do
-
1
expect(successful_response.status).to eq :ok
-
end
-
-
1
it 'returns :error for error responses' do
-
1
expect(error_response.status).to eq :error
-
end
-
-
1
it 'returns :error for unparsable json responses' do
-
1
expect(unparsable_response.status).to eq :error
-
end
-
-
1
it 'returns :error for responses without content' do
-
1
raw_response = ''
-
3
allow(raw_response).to receive(:status) { 204 }
-
1
no_content_response = Contentful::Response.new raw_response
-
1
expect(no_content_response.status).to eq :no_content
-
end
-
end
-
-
1
describe '#error_message' do
-
1
it 'returns contentful error message for contentful errors' do
-
1
expect(error_response.error_message).to eq 'The resource could not be found.'
-
end
-
-
1
it 'returns json parser error message for json parse errors' do
-
1
expect(unparsable_response.error_message).to include 'unexpected token'
-
end
-
-
1
it 'returns a ServiceUnavailable error on a 503' do
-
1
error_response = Contentful::Response.new raw_fixture('not_found', 503)
-
1
expect(error_response.status).to eql :error
-
1
expect(error_response.object).to be_kind_of Contentful::ServiceUnavailable
-
end
-
end
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Space do
-
11
let(:space) { vcr('space') { create_client.space } }
-
-
1
describe 'SystemProperties' do
-
1
it 'has a #sys getter returning a hash with symbol keys' do
-
1
expect(space.sys).to be_a Hash
-
1
expect(space.sys.keys.sample).to be_a Symbol
-
end
-
-
1
it 'has #id' do
-
1
expect(space.id).to eq 'cfexampleapi'
-
end
-
-
1
it 'has #type' do
-
1
expect(space.type).to eq 'Space'
-
end
-
end
-
-
1
describe 'Properties' do
-
1
it 'has #name' do
-
1
expect(space.name).to eq 'Contentful Example API'
-
end
-
-
1
it 'has #locales' do
-
1
expect(space.locales).to be_a Array
-
1
expect(space.locales.first).to be_a Contentful::Locale
-
end
-
end
-
end
-
1
def create_client(options = {})
-
212
Contentful::Client.new({
-
space: 'cfexampleapi',
-
access_token: 'b4c0n73n7fu1',
-
}.merge(options))
-
end
-
1
require 'multi_json'
-
-
1
def raw_fixture(which, status = 200, _as_json = false, headers = {})
-
40
object = Object.new
-
225
allow(object).to receive(:status) { status }
-
125
allow(object).to receive(:headers) { headers }
-
163
allow(object).to receive(:to_s) { File.read File.dirname(__FILE__) + "/../fixtures/json_responses/#{which}.json" }
-
53
allow(object).to receive(:body) { object.to_s }
-
44
allow(object).to receive(:[]) { |key| object.headers[key] }
-
-
40
object
-
end
-
-
1
def json_fixture(which, _as_json = false)
-
1
MultiJson.load(
-
File.read File.dirname(__FILE__) + "/../fixtures/json_responses/#{which}.json"
-
)
-
end
-
1
require 'vcr'
-
-
1
VCR.configure do |c|
-
1
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
-
1
c.ignore_localhost = true
-
1
c.hook_into :webmock
-
1
c.default_cassette_options = { record: :once }
-
end
-
-
1
def vcr(name, &block)
-
191
VCR.use_cassette(name, &block)
-
end
-
-
1
def expect_vcr(name, &block)
-
14
expect { VCR.use_cassette(name, &block) }
-
end
-
1
require 'spec_helper'
-
-
1
describe Contentful::Sync do
-
1
before :each do
-
14
Contentful::ContentTypeCache.clear!
-
end
-
-
1
let(:first_page) do
-
2
vcr('sync_page')do
-
2
create_client.sync(initial: true).first_page
-
end
-
end
-
-
1
let(:last_page) do
-
2
vcr('sync_page')do
-
2
vcr('sync_page_2')do
-
2
create_client.sync(initial: true).first_page.next_page
-
end
-
end
-
end
-
-
1
describe 'environments' do
-
1
it 'works for environments' do
-
1
vcr('sync_environment') {
-
1
page = nil
-
1
expect {
-
1
page = create_client(
-
space: 'a22o2qgm356c',
-
access_token: 'bfbc63cf745a037125dbcc64f716a9a0e9d091df1a79e84920b890f87a6e7ab9',
-
environment: 'staging'
-
).sync(initial: true).first_page
-
}.not_to raise_exception
-
-
1
expect(page.items.first).to be_a ::Contentful::Entry
-
1
expect(page.items.first.environment.id).to eq 'staging'
-
}
-
end
-
end
-
-
1
describe '#initialize' do
-
1
it 'takes an options hash on initialization' do
-
1
expect do
-
2
vcr('sync_deletion') { create_client.sync(initial: true, type: 'Deletion').first_page }
-
end.not_to raise_exception
-
end
-
-
1
it 'takes a next_sync_url on initialization' do
-
1
expect do
-
2
vcr('sync_page_2') { create_client.sync('https://cdn.contentful.com/spaces/cfexampleapi/environments/master/sync?sync_token=w5ZGw6JFwqZmVcKsE8Kow4grw45QdybCr8Okw6AYwqbDksO3ehvDpUPCgcKsKXbCiAwPC8K2w4LDvsOkw6nCjhPDpcOQADElWsOoU8KGR3HCtsOAwqd6wp_Dulp8w6LDsF_CtsK7Kk05wrMvwrLClMOgG2_Dn2sGPg').first_page }
-
end.not_to raise_exception
-
end
-
end
-
-
1
describe '#first_page' do
-
1
it 'returns only the first page of a new sync' do
-
1
vcr('sync_page')do
-
1
expect(create_client.sync(initial: true).first_page).to be_a Contentful::SyncPage
-
end
-
end
-
end
-
-
1
describe '#each_page' do
-
1
it 'iterates through sync pages' do
-
1
sync = create_client.sync(initial: true)
-
2
vcr('sync_page'){ vcr('sync_page_2'){
-
1
count = 0
-
1
sync.each_page do |page|
-
2
expect(page).to be_a Contentful::SyncPage
-
2
count += 1
-
end
-
1
expect(count).to eq 2
-
}}
-
end
-
end
-
-
1
describe '#next_sync_url' do
-
1
it 'is empty if there are still more pages to request in the current sync' do
-
1
expect(first_page.next_sync_url).to be_nil
-
end
-
-
1
it 'returns the url to continue the sync next time' do
-
1
expect(last_page.next_sync_url).to be_a String
-
end
-
end
-
-
1
describe '#completed?' do
-
1
it 'will return true if no more pages to request in the current sync' do
-
1
expect(first_page.next_sync_url).to be_falsey
-
end
-
-
1
it 'will return true if not all pages requested, yet' do
-
1
expect(last_page.next_sync_url).to be_truthy
-
end
-
end
-
-
1
describe '#each_item' do
-
1
it 'will directly iterate through all resources' do
-
1
sync = create_client.sync(initial: true)
-
2
vcr('sync_page'){ vcr('sync_page_2'){
-
1
sync.each_item do |item|
-
14
expect(item).to be_a Contentful::BaseResource
-
end
-
}}
-
end
-
end
-
-
1
describe 'Resource parsing' do
-
1
it 'will correctly parse the `file` field of an asset' do
-
1
sync = create_client.sync(initial: true)
-
1
vcr('sync_page') {
-
15
asset = sync.first_page.items.select { |item| item.is_a?(Contentful::Asset) }.first
-
-
1
expect(asset.file.file_name).to eq 'doge.jpg'
-
1
expect(asset.file.content_type).to eq 'image/jpeg'
-
1
expect(asset.file.details['image']['width']).to eq 5800
-
1
expect(asset.file.details['image']['height']).to eq 4350
-
1
expect(asset.file.details['size']).to eq 522943
-
1
expect(asset.file.url).to eq '//images.contentful.com/cfexampleapi/1x0xpXu4pSGS4OukSyWGUK/cc1239c6385428ef26f4180190532818/doge.jpg'
-
}
-
end
-
end
-
-
1
describe 'raw_mode' do
-
1
before do
-
3
@sync = create_client(raw_mode: true).sync(initial: true)
-
end
-
-
1
it 'should not fail' do
-
1
vcr('sync_page_short') {
-
2
expect { @sync.first_page }.not_to raise_error
-
}
-
end
-
-
1
it 'should return a raw Response' do
-
1
vcr('sync_page_short') {
-
1
expect(@sync.first_page).to be_a Contentful::Response
-
}
-
end
-
-
1
it 'should return JSON' do
-
1
expected = {
-
"sys" => {"type" => "Array"},
-
"items" => [
-
{
-
"sys" => {
-
"space" => {
-
"sys" => {
-
"type" => "Link",
-
"linkType" => "Space",
-
"id" => "cfexampleapi"}
-
},
-
"type" => "Entry",
-
"contentType" => {
-
"sys" => {
-
"type" => "Link",
-
"linkType" => "ContentType",
-
"id" => "1t9IbcfdCk6m04uISSsaIK"
-
}
-
},
-
"id" => "5ETMRzkl9KM4omyMwKAOki",
-
"revision" => 2,
-
"createdAt" => "2014-02-21T13:42:57.752Z",
-
"updatedAt" => "2014-04-16T12:44:02.691Z"
-
},
-
"fields" => {
-
"name" => {"en-US"=>"London"},
-
"center" => {
-
"en-US" => {"lat"=>51.508515, "lon"=>-0.12548719999995228}
-
}
-
}
-
}
-
],
-
"nextSyncUrl" => "https://cdn.contentful.com/spaces/cfexampleapi/environments/master/sync?sync_token=w5ZGw6JFwqZmVcKsE8Kow4grw45QdybCr8Okw6AYwqbDksO3ehvDpUPCgcKsKXbCiAwPC8K2w4LDvsOkw6nCjhPDpcOQADElWsOoU8KGR3HCtsOAwqd6wp_Dulp8w6LDsF_CtsK7Kk05wrMvwrLClMOgG2_Dn2sGPg"
-
}
-
1
vcr('sync_page_short') {
-
1
expect(@sync.first_page.object).to eql expected
-
}
-
end
-
end
-
end