Contentstack 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- contentstack/__init__.py +29 -0
- contentstack/asset.py +784 -0
- contentstack/config.py +162 -0
- contentstack/content_type.py +233 -0
- contentstack/entry.py +1162 -0
- contentstack/errors.py +162 -0
- contentstack/http_connection.py +195 -0
- contentstack/query.py +1632 -0
- contentstack/stack.py +760 -0
- contentstack/utils.py +34 -0
- contentstack-0.1.0.dist-info/LICENSE +21 -0
- contentstack-0.1.0.dist-info/METADATA +150 -0
- contentstack-0.1.0.dist-info/RECORD +15 -0
- contentstack-0.1.0.dist-info/WHEEL +5 -0
- contentstack-0.1.0.dist-info/top_level.txt +1 -0
contentstack/errors.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
Created by Shailesh Mishra on 22/06/19.
|
|
5
|
+
Copyright 2019 Contentstack. All rights reserved.
|
|
6
|
+
|
|
7
|
+
contentstack.error
|
|
8
|
+
~~~~~~~~~~~~~~~~~~
|
|
9
|
+
|
|
10
|
+
API Reference: https://www.contentstack.com/docs/apis/content-delivery-api/#error
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Error:
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
This module implements the Error class.
|
|
19
|
+
|
|
20
|
+
API Reference: https://www.contentstack.com/docs/apis/content-delivery-api/#error
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
|
|
26
|
+
self.__error_dict = {}
|
|
27
|
+
self.__error_code = str
|
|
28
|
+
self.__msg = str
|
|
29
|
+
|
|
30
|
+
def _config(self, result: dict):
|
|
31
|
+
|
|
32
|
+
# _instance is the protected member of the asset, So outsiders can not access this file.
|
|
33
|
+
if result is not None and len(result) > 0:
|
|
34
|
+
self.__error_dict = result
|
|
35
|
+
self.__error_code = self.__error_dict['error_code']
|
|
36
|
+
self.__msg = self.__error_dict['error_message']
|
|
37
|
+
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def error_code(self):
|
|
42
|
+
"""It returns error code from the stack response
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
int -- error_code as int
|
|
46
|
+
|
|
47
|
+
==============================
|
|
48
|
+
|
|
49
|
+
[Example:]
|
|
50
|
+
|
|
51
|
+
>>> error_code = error.error_code
|
|
52
|
+
|
|
53
|
+
==============================
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
return self.__error_code
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def error_message(self):
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
Returns error_message from the stack response
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
str -- error_message from the stack response
|
|
66
|
+
|
|
67
|
+
==============================
|
|
68
|
+
|
|
69
|
+
[Example:]
|
|
70
|
+
|
|
71
|
+
>>> message = error.error_message
|
|
72
|
+
|
|
73
|
+
==============================
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
return self.__msg
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def error(self):
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
error property returns error code and error_message in dict formats
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
dict -- error code and error_message in dict formats
|
|
86
|
+
|
|
87
|
+
==============================
|
|
88
|
+
|
|
89
|
+
[Example:]
|
|
90
|
+
|
|
91
|
+
>>> error = error.error
|
|
92
|
+
|
|
93
|
+
==============================
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
return self.__msg
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def error_info(self) -> dict:
|
|
100
|
+
|
|
101
|
+
"""error information
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
dict -- error information
|
|
105
|
+
|
|
106
|
+
==============================
|
|
107
|
+
|
|
108
|
+
[Example:]
|
|
109
|
+
|
|
110
|
+
>>> ode = error.error_info
|
|
111
|
+
|
|
112
|
+
==============================
|
|
113
|
+
"""
|
|
114
|
+
return self.__error_dict
|
|
115
|
+
|
|
116
|
+
errors_str = {
|
|
117
|
+
|
|
118
|
+
'invalid_json': "Please provide valid JSON.",
|
|
119
|
+
'api_key_is_none': "Stack api key can not be None.",
|
|
120
|
+
'empty_content_type': "Please set contentType name.",
|
|
121
|
+
'access_token_error': "Access token can not be None.",
|
|
122
|
+
'environment_error': "Environment can not be None.",
|
|
123
|
+
'connection_error': "Connection error",
|
|
124
|
+
'auth_failure': "Authentication Not present.",
|
|
125
|
+
'parse_error': "Parsing Error.",
|
|
126
|
+
'server_error': "Server interaction went wrong, Please try again.",
|
|
127
|
+
'error_default': "Oops! Something went wrong. Please try again.",
|
|
128
|
+
'no_network': "Network not available.",
|
|
129
|
+
'query_error': "Please provide valid params."
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
__error_code = {
|
|
133
|
+
|
|
134
|
+
400: "The request was incorrect or corrupted.",
|
|
135
|
+
401: "The login credentials are invalid.",
|
|
136
|
+
403: "The page or resource that is being accessed is forbidden.",
|
|
137
|
+
404: "The requested page or resource could not be found.",
|
|
138
|
+
412: "The entered API key is invalid.",
|
|
139
|
+
422: "The request is syntactically correct but contains semantic errors",
|
|
140
|
+
429: "The number of requests exceeds the allowed limit for the given time period.",
|
|
141
|
+
500: "The server is malfunctioning and is not specific on what the problem is.",
|
|
142
|
+
502: "A server received an invalid response from another server.",
|
|
143
|
+
504: """ A server did not receive a timely response from another server that it was accessing while attempting
|
|
144
|
+
to load the web page or fill another request by the browser. """
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class StackException(Exception):
|
|
149
|
+
"""
|
|
150
|
+
StackException used to handle Stack validation Errors.
|
|
151
|
+
"""
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class RequestsWarning(Warning):
|
|
156
|
+
"""Base warning for Requests."""
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class FileModeWarning(RequestsWarning, DeprecationWarning):
|
|
161
|
+
"""A file was opened in text mode, but Requests determined its binary length."""
|
|
162
|
+
pass
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HttpConnection
|
|
3
|
+
contentstack
|
|
4
|
+
Created by Shailesh Mishra on 22/06/19.
|
|
5
|
+
Copyright 2019 Contentstack. All rights reserved.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import requests
|
|
11
|
+
from urllib.parse import urlencode
|
|
12
|
+
from contentstack import Error
|
|
13
|
+
from json import JSONDecodeError
|
|
14
|
+
from requests.exceptions import Timeout, HTTPError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HTTPConnection(object):
|
|
18
|
+
|
|
19
|
+
def __init__(self, url, query, stack_headers):
|
|
20
|
+
|
|
21
|
+
if None not in (url, query, stack_headers):
|
|
22
|
+
self.__payload = None
|
|
23
|
+
self.__base_url = url
|
|
24
|
+
self.__query_params = query
|
|
25
|
+
self.__headers = stack_headers
|
|
26
|
+
self.__headers.update(self.__user_agents())
|
|
27
|
+
|
|
28
|
+
if 'environment' in self.__headers:
|
|
29
|
+
environment = self.__headers['environment']
|
|
30
|
+
self.__query_params['environment'] = environment
|
|
31
|
+
|
|
32
|
+
def get_result(self, url: str, query: dict, headers: dict):
|
|
33
|
+
|
|
34
|
+
if None not in (url, query, headers):
|
|
35
|
+
if len(url) > 0 and len(self.__headers) > 0:
|
|
36
|
+
self.__base_url = url
|
|
37
|
+
if len(headers) > 0:
|
|
38
|
+
self.__headers.update(headers)
|
|
39
|
+
self.__query_params.update(query)
|
|
40
|
+
|
|
41
|
+
# Case: If UIR Parameter contains entries
|
|
42
|
+
if 'entries' in self.__base_url:
|
|
43
|
+
self.__payload = self.__execute_entry()
|
|
44
|
+
else:
|
|
45
|
+
url_param = ''
|
|
46
|
+
for (key, value) in self.__query_params.items():
|
|
47
|
+
url_param = '{}&{}={}'.format(url_param, key, value)
|
|
48
|
+
self.__payload = url_param
|
|
49
|
+
try:
|
|
50
|
+
if self.__payload.startswith("&"):
|
|
51
|
+
self.__payload = self.__payload[1:]
|
|
52
|
+
|
|
53
|
+
_url = '{}?{}'.format(self.__base_url, self.__payload)
|
|
54
|
+
logging.info('{}?{}'.format(self.__base_url, self.__payload))
|
|
55
|
+
response = requests.get(_url, verify=True, timeout=(10, 8), headers=self.__headers)
|
|
56
|
+
|
|
57
|
+
if response.status_code == 200:
|
|
58
|
+
if response.raise_for_status() is None:
|
|
59
|
+
return self.__parse_dict(response)
|
|
60
|
+
else:
|
|
61
|
+
err = response.json()
|
|
62
|
+
if err is not None:
|
|
63
|
+
error = Error()
|
|
64
|
+
error._config(err)
|
|
65
|
+
return error
|
|
66
|
+
|
|
67
|
+
logging.info('\n\nrequest url => {}\nresponse={}'.format(response.url, response))
|
|
68
|
+
|
|
69
|
+
except Timeout:
|
|
70
|
+
raise TimeoutError('The request timed out')
|
|
71
|
+
except ConnectionError:
|
|
72
|
+
raise ConnectionError('Connection error occurred')
|
|
73
|
+
except JSONDecodeError:
|
|
74
|
+
raise JSONDecodeError('Invalid JSON in request')
|
|
75
|
+
except HTTPError:
|
|
76
|
+
raise HTTPError('Http Error Occurred')
|
|
77
|
+
|
|
78
|
+
def __parse_dict(self, response):
|
|
79
|
+
from contentstack.stack import SyncResult
|
|
80
|
+
|
|
81
|
+
result = response.json()
|
|
82
|
+
if 'stack' in result:
|
|
83
|
+
return result['stack']
|
|
84
|
+
if 'entry' in result:
|
|
85
|
+
dict_entry = result['entry']
|
|
86
|
+
return self.__parse_entries(dict_entry)
|
|
87
|
+
if 'entries' in result:
|
|
88
|
+
entry_list = result['entries']
|
|
89
|
+
return self.__parse_entries(entry_list)
|
|
90
|
+
if 'asset' in result:
|
|
91
|
+
dict_asset = result['asset']
|
|
92
|
+
return self.__parse_assets(dict_asset, 1)
|
|
93
|
+
if 'assets' in result:
|
|
94
|
+
asset_list = result['assets']
|
|
95
|
+
asset_count = 0
|
|
96
|
+
if 'count' in result:
|
|
97
|
+
asset_count = result['count']
|
|
98
|
+
return self.__parse_assets(asset_list, asset_count)
|
|
99
|
+
if 'content_type' in result:
|
|
100
|
+
return result['content_type']
|
|
101
|
+
if 'content_types' in result:
|
|
102
|
+
return result['content_types']
|
|
103
|
+
if 'items' in result:
|
|
104
|
+
sync_result = SyncResult()
|
|
105
|
+
sync_result._configure(result)
|
|
106
|
+
return sync_result
|
|
107
|
+
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def __parse_entries(result):
|
|
112
|
+
from contentstack import Entry
|
|
113
|
+
entries = []
|
|
114
|
+
entry = Entry()
|
|
115
|
+
# if 'count' in result:
|
|
116
|
+
# entry.count = result['count']
|
|
117
|
+
if result is not None and len(result) > 0:
|
|
118
|
+
if isinstance(result, dict):
|
|
119
|
+
return entry._configure(result)
|
|
120
|
+
if isinstance(result, list):
|
|
121
|
+
for entry_obj in result:
|
|
122
|
+
each_entry = Entry()._configure(entry_obj)
|
|
123
|
+
entries.append(each_entry)
|
|
124
|
+
return entries
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def __parse_assets(result, asset_count):
|
|
128
|
+
from contentstack import Asset
|
|
129
|
+
assets = []
|
|
130
|
+
asset = Asset()
|
|
131
|
+
|
|
132
|
+
if result is not None and len(result) > 0:
|
|
133
|
+
if isinstance(result, dict):
|
|
134
|
+
return asset._configure(result)
|
|
135
|
+
if isinstance(result, list):
|
|
136
|
+
for asset_obj in result:
|
|
137
|
+
itr_asset = asset._configure(asset_obj)
|
|
138
|
+
assets.append(itr_asset)
|
|
139
|
+
asset._count(asset_count)
|
|
140
|
+
return assets
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def __user_agents() -> dict:
|
|
144
|
+
|
|
145
|
+
import contentstack
|
|
146
|
+
import platform
|
|
147
|
+
|
|
148
|
+
header = {'sdk': dict(name=contentstack.__package__, version=contentstack.__version__)}
|
|
149
|
+
os = platform.system()
|
|
150
|
+
if os == 'Darwin':
|
|
151
|
+
os = 'macOS'
|
|
152
|
+
elif not os or os == 'Java':
|
|
153
|
+
os = None
|
|
154
|
+
elif os and os not in ['macOS', 'Windows']:
|
|
155
|
+
os = 'Linux'
|
|
156
|
+
header['os'] = {'name': os, 'version': platform.release()}
|
|
157
|
+
package = "contentstack-python, - {}".format(contentstack.__version__)
|
|
158
|
+
local_headers = {'User-Agent': str(header), "Content-Type": 'application/json', "X-User-Agent": package}
|
|
159
|
+
return local_headers
|
|
160
|
+
|
|
161
|
+
def __execute_entry(self):
|
|
162
|
+
|
|
163
|
+
url_param = ''
|
|
164
|
+
|
|
165
|
+
for (key, value) in self.__query_params.items():
|
|
166
|
+
if key == 'include[]':
|
|
167
|
+
if isinstance(value, list):
|
|
168
|
+
url_param = '{}&{}'.format(url_param, urlencode({key: value}, doseq=True))
|
|
169
|
+
elif key == 'only[BASE][]':
|
|
170
|
+
if isinstance(value, list):
|
|
171
|
+
url_param = '{}&{}'.format(url_param, urlencode({key: value}, doseq=True))
|
|
172
|
+
elif key == 'except[BASE][]':
|
|
173
|
+
if isinstance(value, list):
|
|
174
|
+
url_param = '{}&{}'.format(url_param, urlencode({key: value}, doseq=True))
|
|
175
|
+
elif key == 'only':
|
|
176
|
+
for uid in value:
|
|
177
|
+
inner_list = value[uid]
|
|
178
|
+
inner_key = 'only[{}][]'.format(uid)
|
|
179
|
+
if isinstance(inner_list, list):
|
|
180
|
+
url_param = '{}&{}'.format(url_param, urlencode({inner_key: inner_list}, doseq=True))
|
|
181
|
+
elif key == 'except':
|
|
182
|
+
for uid in value:
|
|
183
|
+
inner_list = value[uid]
|
|
184
|
+
inner_key = 'except[{}][]'.format(uid)
|
|
185
|
+
if isinstance(inner_list, list):
|
|
186
|
+
url_param = '{}&{}'.format(url_param, urlencode({inner_key: inner_list}, doseq=True))
|
|
187
|
+
elif key == 'query':
|
|
188
|
+
url_param = '{}&{}={}'.format(url_param, key, value.__str__().replace("\'", "\""))
|
|
189
|
+
print(url_param)
|
|
190
|
+
else:
|
|
191
|
+
url_param = '{}&{}={}'.format(url_param, key, value)
|
|
192
|
+
|
|
193
|
+
return url_param
|
|
194
|
+
|
|
195
|
+
|