Skip to content

Commit 10d17cd

Browse files
- removed print() from tests directory
- changelog.rst latest version date updated
1 parent 08fed21 commit 10d17cd

File tree

12 files changed

+328
-155
lines changed

12 files changed

+328
-155
lines changed

.github/workflows/publish.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Publish to PyPI
2+
on:
3+
release:
4+
types: [ created ]
5+
jobs:
6+
publish:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- name: Checkout
10+
uses: actions/checkout@v2
11+
- name: Set up Python
12+
uses: actions/setup-python@v2
13+
with:
14+
python-version: '3.8'
15+
- name: Install dependencies
16+
run: |
17+
python -m pip install --upgrade pip setuptools wheel
18+
pip install -r requirements.txt
19+
- name: Build package
20+
run: python setup.py sdist bdist_wheel
21+
- name: Publish package
22+
uses: pypa/gh-action-pypi-publish@v1
23+
with:
24+
user: __token__
25+
password: ${{ secrets.PYPI_PASSWORD }}
26+
distributions: sdist bdist_wheel

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
## _v1.8.0_
44

5-
### **Date: 09-MAY-2023**
5+
### **Date: 23-MAY-2023**
66

7-
- AZURE_EU, region support added
8-
- include_metadata support added to asset, entry and query
7+
- AZURE_EU, Region support added
8+
- Include Metadata support added to asset, entry and query
99
- General code improvement clean up
10+
- Updated code for Live Preview
1011

1112
---
1213

changelog.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
*v1.8.0*
66
============
77

8-
**Date: 10-MAY-2023**
8+
**Date: 23-MAY-2023**
99

1010
- AZURE_EU, Region support added
1111
- Include Metadata support added to asset, entry and query
1212
- General code improvement clean up
13-
- Changes done for Live Preview
13+
- Updated code for Live Preview
1414

1515

1616
*v1.7.0*

contentstack/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
The __init__.py files are required to make Python treat the directories as containing
33
packages; this is done to prevent directories with a common name, such as string,
44
from unintentionally hiding valid modules that occur later on the module search path
5-
65
"""
76
from .entry import Entry
87
from .asset import Asset
@@ -14,7 +13,7 @@
1413
__title__ = 'contentstack-python'
1514
__author__ = 'contentstack'
1615
__status__ = 'debug'
17-
__version__ = '1.8.0'
16+
__version__ = 'v1.8.0'
1817
__endpoint__ = 'cdn.contentstack.io'
1918
__email__ = 'mobile@contentstack.com'
2019
__developer_email__ = 'shailesh.mishra@contentstack.com'

contentstack/deep_merge_lp.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class DeepMergeMixin:
2+
3+
def __init__(self, entry_response, lp_response):
4+
self.entry_response = entry_response
5+
self.lp_response = lp_response
6+
7+
for lp_obj in self.lp_response:
8+
uid = lp_obj.get("uid")
9+
matching_objs = [entry_obj for entry_obj in entry_response if entry_obj.get("uid") == uid]
10+
if matching_objs:
11+
for matching_obj in matching_objs:
12+
self._deep_merge(lp_obj, matching_obj)
13+
14+
def _deep_merge(self, source, destination):
15+
for key, value in source.items():
16+
if isinstance(value, dict):
17+
node = destination.setdefault(key, {})
18+
self._deep_merge(value, node)
19+
else:
20+
destination[key] = value
21+
return destination

contentstack/entry.py

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import empty
1010

11+
from contentstack.deep_merge_lp import DeepMergeMixin
1112
from contentstack.entryqueryable import EntryQueryable
1213

1314
log = logging.getLogger(__name__)
@@ -35,9 +36,9 @@ def __init__(self, http_instance, content_type_uid, entry_uid):
3536

3637
def environment(self, environment):
3738
"""
38-
Enter the name of the environment of which the entries needs to be included
39+
Enter the name of the environment of which the entries need to be included
3940
Example: production
40-
:param environment: {str} name of the environment of which the entries needs to be included.
41+
:param environment: {str} name of the environment of which the entries need to be included.
4142
:return: Entry, so you can chain this call.
4243
------------------------------
4344
Example::
@@ -83,7 +84,7 @@ def param(self, key, value):
8384
This method is useful to add additional Query parameters to the entry
8485
:param key: {str} -- key The key as string which needs to be added to an Entry
8586
:param value: {object} -- value The value as string which needs to be added to an Entry
86-
:return: Entry, so you can chain this call.
87+
:return: @Entry, so you can chain this call.
8788
-----------------------------
8889
Example::
8990
@@ -164,15 +165,6 @@ def __get_base_url(self, endpoint=''):
164165
url = f'{self.http_instance.endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}'
165166
return url
166167

167-
def __validate_live_preview(self):
168-
live_preview = self.http_instance.live_preview
169-
if 'enable' in live_preview and live_preview['enable'] \
170-
and self.content_type_id == live_preview['content_type_uid']:
171-
if 'live_preview' in live_preview:
172-
self.entry_param['live_preview'] = live_preview['live_preview']
173-
else:
174-
self.entry_param['live_preview'] = 'init'
175-
176168
def fetch(self):
177169
"""
178170
Fetches the latest version of the entries from stack
@@ -187,19 +179,35 @@ def fetch(self):
187179
>>> result = entry.fetch()
188180
-------------------------------
189181
"""
190-
self.__validate_live_preview()
191182
if 'environment' in self.http_instance.headers:
192183
self.entry_param['environment'] = self.http_instance.headers['environment']
193184
if len(self.entry_queryable_param) > 0:
194185
self.entry_param.update(self.entry_queryable_param)
195-
encoded_string = parse.urlencode(self.entry_param, doseq=True)
196-
url = f'{self.base_url}?{encoded_string}'
197-
self._validate_live_preview()
198-
return self.http_instance.get(url)
199-
200-
def _validate_live_preview(self):
201-
lp = self.http_instance.live_preview
202-
if 'content_type_uid' in lp and lp['content_type_uid'] is not None:
203-
if lp['content_type_uid'] != str(self.content_type_id):
204-
self.http_instance.live_preview['enable'] = False
186+
encoded_str = parse.urlencode(self.entry_param, doseq=True)
187+
url = f'{self.base_url}?{encoded_str}'
188+
self._impl_live_preview()
189+
response = self.http_instance.get(url)
190+
if self.http_instance.live_preview is not None and not 'errors' in response:
191+
self.http_instance.live_preview['entry_response'] = response['entry']
192+
return self._merged_response()
193+
return response
194+
195+
def _impl_live_preview(self):
196+
lv = self.http_instance.live_preview
197+
if lv is not None and lv['enable'] and 'content_type_uid' in lv and lv[
198+
'content_type_uid'] == self.content_type_id:
199+
url = lv['url']
200+
self.http_instance.headers['authorization'] = lv['management_token']
201+
lp_resp = self.http_instance.get(url)
202+
if lp_resp is not None and not 'error_code' in lp_resp:
203+
self.http_instance.live_preview['lp_response'] = lp_resp
204+
return None
205+
return None
206+
207+
def _merged_response(self):
208+
if 'entry_response' in self.http_instance.live_preview and 'lp_response' in self.http_instance.live_preview:
209+
entry_response = self.http_instance.live_preview['entry_response']['entry']
210+
lp_response = self.http_instance.live_preview['lp_response']
211+
merged_response = DeepMergeMixin(entry_response, lp_response)
212+
return merged_response.entry_response
205213
pass

contentstack/https_connection.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
This module implements the Requests API.
33
"""
44

5-
# ************* Module https_connection.py **************
6-
# Your code has been rated at 10.00/10 by pylint
7-
85
import logging
96
import platform
107
import requests
@@ -28,13 +25,9 @@ def __get_os_platform():
2825

2926

3027
def user_agents():
31-
"""Default User Agents for the Https"""
32-
header = {'sdk': dict(
33-
name=contentstack.__package__,
34-
version=contentstack.__version__
35-
),
36-
'os': __get_os_platform,
37-
'Content-Type': 'application/json'}
28+
header = {'sdk': dict(name=contentstack.__package__,
29+
version=contentstack.__version__
30+
), 'os': __get_os_platform, 'Content-Type': 'application/json'}
3831
package = f"{contentstack.__title__}/{contentstack.__version__}"
3932
return {'User-Agent': str(header), "X-User-Agent": package}
4033

@@ -62,14 +55,22 @@ def __init__(self, endpoint, headers, timeout, retry_strategy, live_preview):
6255

6356
def impl_live_preview(self):
6457
if self.live_preview['enable']:
65-
print(self.live_preview)
66-
# Get all the params from live preview and make a request,
67-
# get the data and merger it to the base response
68-
pass
58+
host = self.live_preview['host']
59+
authorization = self.live_preview['authorization']
60+
ct = self.live_preview['content_type_uid']
61+
entry_uid = self.live_preview['entry_uid']
62+
url = f'https://{host}/v3/content_types/{ct}/entries'
63+
if entry_uid is not None:
64+
url = f'{url}/{entry_uid}'
65+
self.headers['authorization'] = authorization
66+
lp_resp = get_request(self.session, url, headers=self.headers, timeout=self.timeout)
67+
if lp_resp is not None and not 'error_code' in lp_resp:
68+
return lp_resp
69+
return None
70+
return None
6971

7072
def get(self, url):
7173
self.headers.update(user_agents())
7274
adapter = HTTPAdapter(max_retries=self.retry_strategy)
7375
self.session.mount('https://', adapter)
74-
self.impl_live_preview()
7576
return get_request(self.session, url, headers=self.headers, timeout=self.timeout)

contentstack/query.py

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import empty
1212

1313
from contentstack.basequery import BaseQuery
14+
from contentstack.deep_merge_lp import DeepMergeMixin
1415
from contentstack.entryqueryable import EntryQueryable
1516

1617
log = logging.getLogger(__name__)
@@ -262,9 +263,21 @@ def include_embedded_items(self):
262263
return self
263264

264265
def include_metadata(self):
265-
"""
266-
include_metadata includes metadata in the response
267-
"""
266+
"""include_metadata instance of Query
267+
includes metadata in the response (Entries and Assets) along with entry/entries details.
268+
:return: Query, so we can chain the call
269+
270+
----------------------------
271+
Example:
272+
273+
>>> import contentstack
274+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
275+
>>> content_type = stack.content_type('content_type_uid')
276+
>>> query = content_type.query()
277+
>>> query = query.include_metadata()
278+
>>> result = query.find()
279+
----------------------------
280+
"""
268281
self.query_params['include_metadata'] = 'true'
269282
return self
270283

@@ -303,15 +316,6 @@ def find_one(self):
303316
self.query_params["limit"] = 1
304317
return self.__execute_network_call()
305318

306-
def __validate_live_preview(self):
307-
live_preview = self.http_instance.live_preview
308-
if 'enable' in live_preview and live_preview['enable'] \
309-
and self.content_type_uid == live_preview['content_type_uid']:
310-
if 'live_preview' in live_preview:
311-
self.query_params['live_preview'] = live_preview['live_preview']
312-
else:
313-
self.query_params['live_preview'] = 'init' # initialise
314-
315319
def __execute_network_call(self):
316320
if len(self.entry_queryable_param) > 0:
317321
self.query_params.update(self.entry_queryable_param)
@@ -321,29 +325,29 @@ def __execute_network_call(self):
321325
self.query_params['environment'] = self.http_instance.headers['environment']
322326
encoded_string = parse.urlencode(self.query_params, doseq=True)
323327
url = f'{self.base_url}?{encoded_string}'
324-
if self.http_instance.live_preview['enable']:
325-
if self.http_instance.live_preview['content_type_uid'] == self.content_type_uid:
326-
_rq = self.http_instance.get(url)['entries']
327-
_preview = self.http_instance.live_preview['resp']
328-
return self._merge_preview(_rq, _preview)
329-
self._validate_live_preview()
330-
return self.http_instance.get(url)
331-
332-
def _validate_live_preview(self):
333-
lp = self.http_instance.live_preview
334-
if 'content_type_uid' in lp and lp['content_type_uid'] is not None:
335-
if lp['content_type_uid'] != str(self.content_type_uid):
336-
self.http_instance.live_preview['enable'] = False
328+
self._impl_live_preview()
329+
response = self.http_instance.get(url)
330+
if self.http_instance.live_preview is not None and not 'errors' in response:
331+
self.http_instance.live_preview['entry_response'] = response['entries']
332+
return self._merged_response()
333+
return response
334+
335+
def _impl_live_preview(self):
336+
lv = self.http_instance.live_preview
337+
if lv is not None and lv['enable'] and 'content_type_uid' in lv and lv[
338+
'content_type_uid'] == self.content_type_uid:
339+
url = lv['url']
340+
self.http_instance.headers['authorization'] = lv['management_token']
341+
lp_resp = self.http_instance.get(url)
342+
if lp_resp is not None and not 'error_code' in lp_resp:
343+
self.http_instance.live_preview['lp_response'] = lp_resp
344+
return None
345+
return None
346+
347+
def _merged_response(self):
348+
if 'entry_response' in self.http_instance.live_preview and 'lp_response' in self.http_instance.live_preview:
349+
entry_response = self.http_instance.live_preview['entry_response']['entries']
350+
lp_response = self.http_instance.live_preview['lp_response']
351+
merged_response = DeepMergeMixin(entry_response, lp_response)
352+
return merged_response.entry_response
337353
pass
338-
339-
# def _merge_preview(self, qresp, _preview):
340-
# if isinstance(qresp, dict):
341-
# if 'uid' in qresp and qresp['uid'] == _preview['uid']:
342-
# merged = {**qresp, **_preview} # TODO: Check merging is properly written or not
343-
# else:
344-
# for key in dict.keys():
345-
# qresp[key] = self._merge_preview(qresp[key])
346-
# elif isinstance(qresp, list):
347-
# for index, it in enumerate(qresp):
348-
# qresp[index] = self._merge_preview(it, _preview)
349-
# return qresp

contentstack/stack.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ def live_preview_query(self, **kwargs):
341341
for key, value in kwargs.iteritems():
342342
print "%s = %s" % (key, value)
343343
Uses:=>
344-
live_preview_query (
344+
live_preview_query = (
345345
'enable': True,
346346
'live_preview': '#*#*#*#*#',
347347
'host': 'your_host',
@@ -350,15 +350,28 @@ def live_preview_query(self, **kwargs):
350350
'authorization': 'management_token'
351351
)
352352
"""
353-
if self.live_preview is not None and self.live_preview['enable']:
354-
query = kwargs['live_preview']
353+
354+
if self.live_preview is not None and self.live_preview['enable'] and 'live_preview_query' in kwargs:
355+
self.live_preview.update(**kwargs['live_preview_query'])
356+
query = kwargs['live_preview_query']
355357
if query is not None:
356358
self.live_preview['live_preview'] = query['live_preview']
357359
else:
358360
self.live_preview['live_preview'] = 'init'
359-
360361
if 'content_type_uid' in self.live_preview and self.live_preview['content_type_uid'] is not None:
361362
self.live_preview['content_type_uid'] = query['content_type_uid']
362363
if 'entry_uid' in self.live_preview and self.live_preview['entry_uid'] is not None:
363364
self.live_preview['entry_uid'] = query['entry_uid']
365+
self._cal_url()
364366
return self
367+
368+
def _cal_url(self):
369+
host = self.live_preview['host']
370+
ct = self.live_preview['content_type_uid']
371+
url = f'https://{host}/v3/content_types/{ct}/entries'
372+
if 'entry_uid' in self.live_preview:
373+
uid = self.live_preview['entry_uid']
374+
lv = self.live_preview['live_preview']
375+
url = f'{url}/{uid}?live_preview={lv}'
376+
self.live_preview['url'] = url
377+
pass

0 commit comments

Comments
 (0)