Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# CHANGELOG

## Unreleased
### Added
* Added support of `environments` functionality.

## v1.6.0
### Added
Expand Down
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Client Configuration Options

``default_locale``: (optional) Default Locale for your Space, defaults to 'en-US'.

``environment``: (optional) Default Environment for client, defaults to 'master'.

``https``: (optional) Boolean determining wether to use https or http, defaults to True.

``authorization_as_header``: (optional) Boolean determining wether to send access_token through a header or via GET params, defaults to True.
Expand Down
3 changes: 1 addition & 2 deletions contentful/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,9 @@ def incoming_references(self, client=None, query={}):
if client is None:
return False

query.update({ 'links_to_asset': self.id })
query.update({'links_to_asset': self.id})
return client.entries(query)


def __repr__(self):
return "<Asset id='{0}' url='{1}'>".format(
self.sys.get('id', ''),
Expand Down
57 changes: 50 additions & 7 deletions contentful/client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import requests
import platform
from re import sub
from .utils import ConfigurationException, retry_request, string_class
from .utils import ConfigurationException, NotSupportedException
from .utils import retry_request, string_class
from .errors import get_error, RateLimitExceededError, EntryNotFoundError
from .resource_builder import ResourceBuilder
from .content_type_cache import ContentTypeCache
Expand Down Expand Up @@ -32,6 +33,8 @@ class Client(object):
:param api_version: (optional) Target version of the Contentful API.
:param default_locale: (optional) Default Locale for your Space,
defaults to 'en-US'.
:param environment: (optional) Default Environment for client, defaults
to 'master'.
:param https: (optional) Boolean determining wether to use https
or http, defaults to True.
:param authorization_as_header: (optional) Boolean determining wether
Expand Down Expand Up @@ -79,6 +82,7 @@ def __init__(
api_url='cdn.contentful.com',
api_version=1,
default_locale='en-US',
environment='master',
https=True,
authorization_as_header=True,
raw_mode=False,
Expand All @@ -101,6 +105,7 @@ def __init__(
self.api_url = api_url
self.api_version = api_version
self.default_locale = default_locale
self.environment = environment
self.https = https
self.authorization_as_header = authorization_as_header
self.raw_mode = raw_mode
Expand Down Expand Up @@ -156,7 +161,9 @@ def content_type(self, content_type_id, query=None):
"""

return self._get(
'/content_types/{0}'.format(content_type_id),
self.environment_url(
'/content_types/{0}'.format(content_type_id)
),
query
)

Expand All @@ -178,7 +185,7 @@ def content_types(self, query=None):
"""

return self._get(
'/content_types',
self.environment_url('/content_types'),
query
)

Expand All @@ -204,7 +211,7 @@ def entry(self, entry_id, query=None):
try:
query.update({'sys.id': entry_id})
return self._get(
'/entries',
self.environment_url('/entries'),
query
)[0]
except IndexError:
Expand Down Expand Up @@ -240,7 +247,7 @@ def entries(self, query=None):
self._normalize_select(query)

return self._get(
'/entries',
self.environment_url('/entries'),
query
)

Expand All @@ -260,7 +267,9 @@ def asset(self, asset_id, query=None):
"""

return self._get(
'/assets/{0}'.format(asset_id),
self.environment_url(
'/assets/{0}'.format(asset_id)
),
query
)

Expand All @@ -286,7 +295,30 @@ def assets(self, query=None):
self._normalize_select(query)

return self._get(
'/assets',
self.environment_url('/assets'),
query
)

def locales(self, query=None):
"""Fetches all Locales from the Environment (up to the set limit, can be modified in `query`).

# TODO: fix url
API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/assets/assets-collection/get-all-assets-of-a-space

:param query: (optional) Dict with API options.
:return: List of :class:`Locale <contentful.locale.Locale>` objects.
:rtype: List of contentful.locale.Locale

Usage:
>>> locales = client.locales()
[<Locale[English (United States)] code='en-US' default=True fallback_code=None optional=False>]
"""

if query is None:
query = {}

return self._get(
self.environment_url('/locales'),
query
)

Expand All @@ -304,6 +336,9 @@ def sync(self, query=None):
<SyncPage next_sync_token='w5ZGw6JFwqZmVcKsE8Kow4grw45QdybC...'>
"""

if self.environment != 'master':
raise NotSupportedException('The sync endpoint is only available for the master environment.')

if query is None:
query = {}
self._normalize_sync(query)
Expand All @@ -313,6 +348,14 @@ def sync(self, query=None):
query
)

def environment_url(self, url):
"""Formats the URL with the environment."""

return "/environments/{0}{1}".format(
self.environment,
url
)

def _normalize_select(self, query):
"""
If the query contains the :select operator, we enforce :sys properties.
Expand Down
2 changes: 1 addition & 1 deletion contentful/content_type_field.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .utils import snake_case
from .content_type_field_types import * # noqa: F401
from .content_type_field_types import * # noqa: F401, F403

"""
contentful.content_type_field
Expand Down
2 changes: 1 addition & 1 deletion contentful/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def incoming_references(self, client=None, query={}):
if client is None:
return False

query.update({ 'links_to_entry': self.id })
query.update({'links_to_entry': self.id})
return client.entries(query)

def __repr__(self):
Expand Down
6 changes: 1 addition & 5 deletions contentful/errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import json


"""
contentful.errors
~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -88,7 +85,7 @@ def _handle_detail(detail):
return detail.get('details', None)

inner_details = [_handle_detail(detail) for detail in details['errors']]
inner_details = [detail for detail in inner_details if detail is not None] # This works in both Py2 and Py3
inner_details = [detail for detail in inner_details if detail is not None] # This works in both Py2 and Py3
return "\n\t".join(inner_details)


Expand All @@ -109,7 +106,6 @@ class AccessDeniedError(HTTPError):
def _default_error_message(self):
return "The specified token does not have access to the requested resource."


def _handle_details(self, details):
return "\n\tReasons:\n\t\t{0}".format("\n\t\t".join(details['reasons']))

Expand Down
23 changes: 15 additions & 8 deletions contentful/locale.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .resource import Resource
"""
contentful.locale
~~~~~~~~~~~~~~~~~
Expand All @@ -11,20 +12,26 @@
"""


class Locale(object):
class Locale(Resource):
"""
API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/localization
"""

def __init__(self, locale_data):
self.code = locale_data.get('code', '')
self.name = locale_data.get('name', '')
self.fallback_code = locale_data.get('fallbackCode', '')
self.default = locale_data.get('default', False)
def __init__(self, item, **kwargs):
super(Locale, self).__init__(item, **kwargs)
self.code = item.get('code', '')
self.name = item.get('name', '')
self.fallback_code = item.get('fallbackCode', '')
self.default = item.get('default', False)
self.optional = item.get('optional', False)

def __repr__(self):
return "<Locale[{0}] code='{1}' default={2}>".format(
return "<Locale[{0}] code='{1}' default={2} fallback_code={3} optional={4}>".format(
self.name,
self.code,
self.default
self.default,
"'{0}'".format(
self.fallback_code
) if self.fallback_code is not None else 'None',
self.optional
)
2 changes: 1 addition & 1 deletion contentful/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(

def _hydrate_sys(self, item):
sys = {}
for k, v in item['sys'].items():
for k, v in item.get('sys', {}).items():
if k in ['space', 'contentType']:
v = self._build_link(v)
if k in ['createdAt', 'updatedAt', 'deletedAt']:
Expand Down
16 changes: 9 additions & 7 deletions contentful/resource_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .content_type import ContentType # noqa: F401
from .deleted_asset import DeletedAsset # noqa: F401
from .deleted_entry import DeletedEntry # noqa: F401
from .locale import Locale # noqa: F401
from .sync_page import SyncPage
from .utils import unresolvable

Expand Down Expand Up @@ -63,11 +64,11 @@ def _build_array(self):
errors = self._errors()

items = [self._build_item(
item,
includes=includes,
errors=errors
) for item in self.json['items']
if not unresolvable(item, self._errors())]
item,
includes=includes,
errors=errors
) for item in self.json['items']
if not unresolvable(item, self._errors())]

return Array(self.json, items)

Expand All @@ -83,7 +84,8 @@ def _build_item(self, item, includes=None, errors=None):
'ContentType',
'Space',
'DeletedEntry',
'DeletedAsset'
'DeletedAsset',
'Locale'
]
if item['sys']['type'] in buildables:
return globals()[item['sys']['type']](
Expand All @@ -101,7 +103,7 @@ def _includes(self):
for e in ['Entry', 'Asset']:
if e in self.json.get('includes', {}):
includes += [item for item in self.json['includes'].get(e, [])
if not unresolvable(item, self._errors())]
if not unresolvable(item, self._errors())]
return includes

def _errors(self):
Expand Down
1 change: 0 additions & 1 deletion contentful/sync_page.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#from urllib.parse import urlsplit, parse_qs
from six.moves.urllib.parse import urlsplit, parse_qs

from .resource import Resource
Expand Down
5 changes: 5 additions & 0 deletions contentful/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ class ConfigurationException(Exception):
pass


class NotSupportedException(Exception):
"""This exception is thrown when something is not supported by the API."""
pass


class retry_request(object):
"""
Decorator to retry function calls in case they raise rate limit exceptions
Expand Down
2 changes: 1 addition & 1 deletion fixtures/cache/cache.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interactions:
Content-Type: [application/vnd.contentful.delivery.v1+json]
User-Agent: [PythonContentfulClient/0.1.0]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/content_types
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/content_types
response:
body:
string: !!binary |
Expand Down
6 changes: 3 additions & 3 deletions fixtures/client/array_endpoints.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interactions:
Content-Type: [application/vnd.contentful.delivery.v1+json]
User-Agent: [PythonContentfulClient/1.0.3]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/content_types
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/content_types
response:
body:
string: !!binary |
Expand Down Expand Up @@ -64,7 +64,7 @@ interactions:
Content-Type: [application/vnd.contentful.delivery.v1+json]
User-Agent: [PythonContentfulClient/1.0.3]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/entries
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/entries
response:
body:
string: !!binary |
Expand Down Expand Up @@ -138,7 +138,7 @@ interactions:
Content-Type: [application/vnd.contentful.delivery.v1+json]
User-Agent: [PythonContentfulClient/1.0.3]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/assets
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/assets
response:
body:
string: !!binary |
Expand Down
2 changes: 1 addition & 1 deletion fixtures/client/asset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interactions:
Content-Type: [application/vnd.contentful.delivery.v1+json]
User-Agent: [PythonContentfulClient/0.1.0]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/assets/nyancat
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/assets/nyancat
response:
body: {string: "{\n \"sys\": {\n \"space\": {\n \"sys\": {\n \"type\":
\"Link\",\n \"linkType\": \"Space\",\n \"id\": \"cfexampleapi\"\n
Expand Down
4 changes: 2 additions & 2 deletions fixtures/client/asset_incoming_references.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interactions:
X-Contentful-User-Agent: [sdk contentful.py/1.4.3; platform python/3.6.4; os
macOS/17.3.0;]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/assets/nyancat
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/assets/nyancat
response:
body: {string: "{\n \"sys\": {\n \"space\": {\n \"sys\": {\n \"type\":
\"Link\",\n \"linkType\": \"Space\",\n \"id\": \"cfexampleapi\"\n
Expand Down Expand Up @@ -59,7 +59,7 @@ interactions:
X-Contentful-User-Agent: [sdk contentful.py/1.4.3; platform python/3.6.4; os
macOS/17.3.0;]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/entries?links_to_asset=nyancat
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/entries?links_to_asset=nyancat
response:
body:
string: !!binary |
Expand Down
2 changes: 1 addition & 1 deletion fixtures/client/assets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interactions:
Content-Type: [application/vnd.contentful.delivery.v1+json]
User-Agent: [PythonContentfulClient/0.1.0]
method: GET
uri: https://cdn.contentful.com/spaces/cfexampleapi/assets
uri: https://cdn.contentful.com/spaces/cfexampleapi/environments/master/assets
response:
body:
string: !!binary |
Expand Down
Loading