Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 3 additions & 2 deletions contentstack/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import logging
from urllib import parse
from contentstack.error_messages import ErrorMessages

class Asset:
r"""`Asset` refer to all the media files (images, videos, PDFs, audio files, and so on)."""
Expand All @@ -15,7 +16,7 @@ def __init__(self, http_instance, uid=None, logger=None):
self.asset_params = {}
self.__uid = uid
if self.__uid is None or self.__uid.strip() == 0:
raise KeyError('Please provide valid uid')
raise KeyError(ErrorMessages.INVALID_UID)
self.base_url = f'{self.http_instance.endpoint}/assets/{self.__uid}'
if 'environment' in self.http_instance.headers:
self.asset_params['environment'] = self.http_instance.headers['environment']
Expand Down Expand Up @@ -68,7 +69,7 @@ def params(self, key, value):
-----------------------------
"""
if None in (key, value) or not isinstance(key, str):
raise KeyError('Kindly provide valid params')
raise KeyError(ErrorMessages.INVALID_PARAMS)
self.asset_params[key] = value
return self

Expand Down
15 changes: 10 additions & 5 deletions contentstack/basequery.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import enum
import logging
from contentstack.error_messages import ErrorMessages

class QueryOperation(enum.Enum):
"""
Expand Down Expand Up @@ -137,8 +138,10 @@ def param(self, key: str, value):

-----------------------------------
"""
if None in (key, value):
raise KeyError('Invalid key or value')
if key is None:
raise KeyError(ErrorMessages.INVALID_KEY)
if value is None:
raise KeyError(ErrorMessages.INVALID_VALUE)
self.query_params[key] = str(value)
return self

Expand All @@ -164,8 +167,10 @@ def query(self, key: str, value):
Returns:
self-- Class instance, So that method chaining can be performed
"""
if None in (key, value):
raise KeyError('Invalid key or value')
if key is None:
raise KeyError(ErrorMessages.INVALID_KEY)
if value is None:
raise KeyError(ErrorMessages.INVALID_VALUE)
self.parameters[key] = str(value)
return self

Expand All @@ -186,7 +191,7 @@ def remove_param(self, key: str):
----------------------------------
"""
if key is None:
raise ValueError('Kindly provide valid key')
raise ValueError(ErrorMessages.INVALID_KEY)
if key in self.query_params:
self.query_params.pop(key, None)
return self
12 changes: 5 additions & 7 deletions contentstack/contenttype.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import json
import logging
from urllib import parse
from contentstack.error_messages import ErrorMessages

from contentstack.entry import Entry
from contentstack.query import Query
Expand Down Expand Up @@ -46,11 +47,9 @@ def entry(self, entry_uid: str):
--------------------------------
"""
if self.__content_type_uid is None:
raise PermissionError('Please provide valid content_type_uid')
raise PermissionError(ErrorMessages.INVALID_CONTENT_TYPE_UID)
if entry_uid is None:
raise PermissionError(json.dumps({
"message": 'Please provide valid entry uid',
"message_detail": 'Entry UID can not be None'}))
raise PermissionError(ErrorMessages.INVALID_UID)
entry = Entry(self.http_instance,
self.__content_type_uid, entry_uid=entry_uid)
return entry
Expand All @@ -69,7 +68,7 @@ def query(self):
------------------------------
"""
if self.__content_type_uid is None:
raise PermissionError('Kindly provide content_type_uid')
raise PermissionError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED)
return Query(self.http_instance, self.__content_type_uid)

def fetch(self):
Expand All @@ -87,8 +86,7 @@ def fetch(self):
------------------------------
"""
if self.__content_type_uid is None:
raise KeyError(
'content_type_uid can not be None to fetch contenttype')
raise KeyError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED)
self.local_param['environment'] = self.http_instance.headers['environment']
uri = f'{self.http_instance.endpoint}/content_types/{self.__content_type_uid}'
encoded_params = parse.urlencode(self.local_param)
Expand Down
5 changes: 3 additions & 2 deletions contentstack/controller.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import requests
from requests.utils import guess_json_utf
from contentstack.error_messages import ErrorMessages


class RequestError(Exception):
Expand All @@ -18,14 +19,14 @@ def get_request(session, url, headers, timeout):
response.encoding = 'utf-8'
except requests.exceptions.RequestException as e:
error = {
'error': f"Failed to connect to {url}: {str(e)}",
'error': ErrorMessages.CONNECTION_FAILED.format(url=url, error=str(e)),
'error_code': '400',
'error_message': {str(e)}
}
raise RequestError(error)
except Exception as e:
error = {
'error': f"An unexpected error while making request to {url}, '400', {str(e)}",
'error': ErrorMessages.OPERATION_FAILED.format(url=url, error=str(e)),
'error_code': '400',
'error_message': {str(e)}
}
Expand Down
4 changes: 3 additions & 1 deletion contentstack/deep_merge_lp.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from contentstack.error_messages import ErrorMessages

class DeepMergeMixin:

def __init__(self, entry_response, lp_response):
if not isinstance(entry_response, list) or not isinstance(lp_response, list):
raise TypeError("Both entry_response and lp_response must be lists of dictionaries")
raise TypeError(ErrorMessages.INVALID_RESPONSE_TYPE)

self.entry_response = entry_response
self.lp_response = lp_response
Expand Down
18 changes: 9 additions & 9 deletions contentstack/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#min-similarity-lines=10
import logging
from urllib import parse
from contentstack.error_messages import ErrorMessages

from contentstack.deep_merge_lp import DeepMergeMixin
from contentstack.entryqueryable import EntryQueryable
Expand Down Expand Up @@ -49,7 +50,7 @@ def environment(self, environment):
------------------------------
"""
if environment is None:
raise KeyError('Kindly provide a valid environment')
raise KeyError(ErrorMessages.INVALID_ENVIRONMENT)
self.http_instance.headers['environment'] = environment
return self

Expand All @@ -72,7 +73,7 @@ def version(self, version):
------------------------------
"""
if version is None:
raise KeyError('Kindly provide a valid version')
raise KeyError(ErrorMessages.INVALID_VERSION)
self.entry_param['version'] = version
return self

Expand All @@ -94,7 +95,7 @@ def param(self, key, value):
-----------------------------
"""
if None in (key, value) and not isinstance(key, str):
raise ValueError('Kindly provide valid key and value arguments')
raise ValueError(ErrorMessages.INVALID_KEY_VALUE_ARGS)
self.entry_param[key] = value
return self

Expand All @@ -113,7 +114,7 @@ def include_fallback(self):
>>> result = entry.fetch()
----------------------------
"""
print('Requesting fallback....')
print(ErrorMessages.REQUESTING_FALLBACK)
self.entry_param['include_fallback'] = 'true'
return self

Expand Down Expand Up @@ -157,8 +158,7 @@ def __get_base_url(self, endpoint=''):
if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace
self.http_instance.endpoint = endpoint
if None in (self.http_instance, self.content_type_id, self.entry_uid):
raise KeyError(
'Provide valid http_instance, content_type_uid or entry_uid')
raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE)
url = f'{self.http_instance.endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}'
return url

Expand Down Expand Up @@ -217,12 +217,12 @@ def _merged_response(self):
if not isinstance(lp_entry, list):
lp_entry = [lp_entry] # Wrap in a list if it's a dict
if not all(isinstance(item, dict) for item in entry_response):
raise TypeError(f"entry_response must be a list of dictionaries. Got: {entry_response}")
raise TypeError(ErrorMessages.INVALID_ENTRY_RESPONSE)
if not all(isinstance(item, dict) for item in lp_entry):
raise TypeError(f"lp_entry must be a list of dictionaries. Got: {lp_entry}")
raise TypeError(ErrorMessages.INVALID_LP_ENTRY)
merged_response = DeepMergeMixin(entry_response, lp_entry).to_dict() # Convert to dictionary
return merged_response # Now correctly returns a dictionary
raise ValueError("Missing required keys in live_preview data")
raise ValueError(ErrorMessages.MISSING_LIVE_PREVIEW_KEYS)

def variants(self, variant_uid: str | list[str], params: dict = None):
"""
Expand Down
5 changes: 3 additions & 2 deletions contentstack/entryqueryable.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
that is used as parents class for the query and entry classes
"""
import logging
from contentstack.error_messages import ErrorMessages

class EntryQueryable:
"""
Expand Down Expand Up @@ -55,7 +56,7 @@ def only(self, field_uid: str):
if isinstance(field_uid, str):
self.entry_queryable_param['only[BASE][]'] = field_uid
else:
raise KeyError("Invalid field_uid provided")
raise KeyError(ErrorMessages.INVALID_FIELD_UID)
return self

def excepts(self, field_uid: str):
Expand All @@ -69,7 +70,7 @@ def excepts(self, field_uid: str):
if isinstance(field_uid, str):
self.entry_queryable_param['except[BASE][]'] = field_uid
else:
raise KeyError("Invalid field_uid provided")
raise KeyError(ErrorMessages.INVALID_FIELD_UID)
return self

def include_reference(self, field_uid):
Expand Down
59 changes: 59 additions & 0 deletions contentstack/error_messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
Centralized error messages for the Contentstack Python SDK.
All error messages should be defined here and imported where needed.
"""

class ErrorMessages:
# BaseQuery errors
INVALID_KEY = "Invalid key. Provide a valid key and try again."
INVALID_VALUE = "Invalid value. Provide a valid value and try again."
INVALID_KEY_OR_VALUE = "Invalid key or value. Provide valid values and try again."

# ContentType errors
INVALID_CONTENT_TYPE_UID = "Content type UID is invalid. Provide a valid UID and try again."
CONTENT_TYPE_UID_REQUIRED = "Content type UID is required. Provide a UID and try again."

# EntryQueryable errors
INVALID_FIELD_UID = "Invalid field UID. Provide a valid UID and try again."

# DeepMergeLp errors
INVALID_RESPONSE_TYPE = "Invalid input. entry_response and lp_response must be lists of dictionaries. Update the values and try again."

# Entry errors
INVALID_ENVIRONMENT = "Invalid environment. Provide a valid environment and try again."
INVALID_VERSION = "Invalid version. Provide a valid version and try again."
INVALID_KEY_VALUE_ARGS = "Invalid key or value arguments. Provide valid values and try again."
REQUESTING_FALLBACK = "Requesting fallback content for the specified locale."
INVALID_ENTRY_RESPONSE = "Invalid entry_response format. Provide a list of dictionaries, each containing entry data, and try again."
INVALID_LP_ENTRY = "Invalid lp_entry. Provide a list of dictionaries and try again."
MISSING_LIVE_PREVIEW_KEYS = "Missing required keys in live preview data. Provide all required keys and try again."

# Asset errors
INVALID_UID = "Invalid UID. Provide a valid UID and try again."
INVALID_PARAMS = "Invalid parameters. Provide valid parameters and try again."

# Controller errors
CONNECTION_FAILED = "Connection failed. Unable to connect to {url}. Error: {error}. Check your connection and try again."
OPERATION_FAILED = "Operation failed. An unexpected error occurred while making request to {url}. Error: {error}. Check your inputs and try again."

# Query errors
DEPRECATED_SEARCH = """The search() method is deprecated since version 1.7.0. Use regex() instead.
Example: query.regex("title", "^Blog.*") to search for titles starting with "Blog"."""
INVALID_JSON = "Invalid JSON. Error: {error}. Provide valid JSON and try again."
MISSING_ENTRIES_KEY = "Invalid response. The 'entries' key is missing. Include the 'entries' key and try again."
MISSING_ENTRY_KEY = "Invalid lp_response. The 'entry' key is missing. Include the 'entry' key and try again."

# Variants errors
ENTRY_UID_REQUIRED = "Missing entry UID. Provide a valid UID and try again."

# Stack errors
INVALID_STACK_UID = "Invalid UID. Provide a valid UID and try again."

# Utility errors
INVALID_PARAMS_TYPE = "Invalid params. Provide a dictionary and try again."
INVALID_URL_PARAMS = "Invalid input. Provide base_url as a string and params as a dictionary, then try again."

# Stack errors
INVALID_API_KEY = "Invalid API key. Provide a valid API key and try again."
INVALID_DELIVERY_TOKEN = "Invalid delivery token. Provide a valid delivery token and try again."
INVALID_ENVIRONMENT_TOKEN = "Invalid environment. Provide a valid environment and try again."
21 changes: 10 additions & 11 deletions contentstack/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import json
import logging
import warnings
from contentstack.error_messages import ErrorMessages
from urllib import parse

from contentstack.basequery import BaseQuery
Expand Down Expand Up @@ -44,8 +45,7 @@ def __init__(self, http_instance, content_type_uid, logger=None):
self.content_type_uid = content_type_uid
self.http_instance = http_instance
if self.content_type_uid is None:
raise PermissionError(
'You are not allowed here without content_type_uid')
raise PermissionError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED)
self.base_url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries'
self.base_url = self.__get_base_url()
self.logger = logger or logging.getLogger(__name__)
Expand All @@ -54,8 +54,7 @@ def __get_base_url(self, endpoint=''):
if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace
self.http_instance.endpoint = endpoint
if None in (self.http_instance, self.content_type_uid):
raise KeyError(
'Provide valid http_instance, content_type_uid or entry_uid')
raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE)
url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries'

return url
Expand Down Expand Up @@ -138,7 +137,7 @@ def search(self, value: str):
>>> result = query.find()
-------------------------------------
"""
warnings.warn('deprecated in 1.7.0, Use regex function instead')
warnings.warn(ErrorMessages.DEPRECATED_SEARCH)
if value is not None:
self.query_params["typeahead"] = value
return self
Expand Down Expand Up @@ -170,7 +169,7 @@ def where_in(self, key: str, query_object):
self.query_params["query"] = {
key: {"$in_query": query_object.parameters}}
else:
raise ValueError('Invalid Key or Value provided')
raise ValueError(ErrorMessages.INVALID_KEY_OR_VALUE)
return self

def where_not_in(self, key, query_object):
Expand Down Expand Up @@ -200,7 +199,7 @@ def where_not_in(self, key, query_object):
self.query_params["query"] = {
key: {"$nin_query": query_object.parameters}}
else:
raise ValueError('Invalid Key or Value provided')
raise ValueError(ErrorMessages.INVALID_KEY_OR_VALUE)
return self

def include_fallback(self):
Expand Down Expand Up @@ -330,14 +329,14 @@ def __execute_network_call(self):
try:
response = json.loads(response) # Convert JSON string to dictionary
except json.JSONDecodeError as e:
print(f"JSON decode error: {e}")
print(ErrorMessages.INVALID_JSON.format(error=str(e)))
return {"error": "Invalid JSON response"} # Return an error dictionary

if self.http_instance.live_preview is not None and 'errors' not in response:
if 'entries' in response:
self.http_instance.live_preview['entry_response'] = response['entries'][0] # Get first entry
else:
print(f"Error: 'entries' key missing in response: {response}")
print(ErrorMessages.MISSING_ENTRIES_KEY)
return {"error": "'entries' key missing in response"}
return self._merged_response()
return response
Expand All @@ -356,7 +355,7 @@ def _impl_live_preview(self):
if 'entry' in lp_resp:
self.http_instance.live_preview['lp_response'] = {'entry': lp_resp['entry']} # Extract entry
else:
print(f"Warning: Missing 'entry' key in lp_response: {lp_resp}")
print(ErrorMessages.MISSING_ENTRY_KEY)
return None
return None

Expand All @@ -368,4 +367,4 @@ def _merged_response(self):
merged_response = DeepMergeMixin(entry_response, lp_response)
return merged_response # Return the merged dictionary

raise ValueError("Missing required keys in live_preview data")
raise ValueError(ErrorMessages.MISSING_LIVE_PREVIEW_KEYS)
Loading
Loading