From 04803c37db166a369632094e9619761618278d86 Mon Sep 17 00:00:00 2001 From: Aniket Shikhare <62753263+AniketDev7@users.noreply.github.com> Date: Fri, 4 Jul 2025 03:49:12 +0530 Subject: [PATCH 1/7] Refactor tests to use config.py, remove hardcoded credentials, and comment deprecated/obsolete tests --- tests/test_entry.py | 58 +++++++++++++------------------------- tests/test_live_preview.py | 6 ++-- tests/test_stack.py | 13 +++++---- 3 files changed, 30 insertions(+), 47 deletions(-) diff --git a/tests/test_entry.py b/tests/test_entry.py index 990be80..fe30466 100644 --- a/tests/test_entry.py +++ b/tests/test_entry.py @@ -3,11 +3,11 @@ import config import contentstack -_UID = 'blt53ca1231625bdde4' API_KEY = config.APIKEY DELIVERY_TOKEN = config.DELIVERYTOKEN ENVIRONMENT = config.ENVIRONMENT HOST = config.HOST +FAQ_UID = config.FAQ_UID # Add this in your config.py class TestEntry(unittest.TestCase): @@ -19,69 +19,54 @@ def test_run_initial_query(self): query = self.stack.content_type('faq').query() result = query.find() if result is not None: - self._UID = result['entries'][0]['uid'] - print(f'the uid is: {_UID}') + self.faq_uid = result['entries'][0]['uid'] + print(f'the uid is: {self.faq_uid}') def test_entry_by_UID(self): - global _UID - entry = self.stack.content_type('faq').entry(_UID) + entry = self.stack.content_type('faq').entry(FAQ_UID) result = entry.fetch() if result is not None: - _UID = result['entry']['uid'] - self.assertEqual(_UID, result['entry']['uid']) + self.assertEqual(FAQ_UID, result['entry']['uid']) def test_03_entry_environment(self): - global _UID - entry = self.stack.content_type('faq').entry( - _UID).environment('test') + entry = self.stack.content_type('faq').entry(FAQ_UID).environment('test') self.assertEqual("test", entry.http_instance.headers['environment']) def test_04_entry_locale(self): - global _UID - entry = self.stack.content_type('faq').entry(_UID).locale('en-ei') + entry = self.stack.content_type('faq').entry(FAQ_UID).locale('en-ei') entry.fetch() self.assertEqual('en-ei', entry.entry_param['locale']) def test_05_entry_version(self): - global _UID - entry = self.stack.content_type('faq').entry(_UID).version(3) + entry = self.stack.content_type('faq').entry(FAQ_UID).version(3) entry.fetch() self.assertEqual(3, entry.entry_param['version']) def test_06_entry_params(self): - global _UID - entry = self.stack.content_type('faq').entry( - _UID).param('param_key', 'param_value') + entry = self.stack.content_type('faq').entry(FAQ_UID).param('param_key', 'param_value') entry.fetch() self.assertEqual('param_value', entry.entry_param['param_key']) def test_07_entry_base_only(self): - global _UID - entry = self.stack.content_type( - 'faq').entry(_UID).only('field_UID') + entry = self.stack.content_type('faq').entry(FAQ_UID).only('field_UID') entry.fetch() self.assertEqual({'environment': 'development', 'only[BASE][]': 'field_UID'}, entry.entry_param) def test_08_entry_base_excepts(self): - global _UID - entry = self.stack.content_type('faq').entry( - _UID).excepts('field_UID') + entry = self.stack.content_type('faq').entry(FAQ_UID).excepts('field_UID') entry.fetch() self.assertEqual({'environment': 'development', 'except[BASE][]': 'field_UID'}, entry.entry_param) def test_10_entry_base_include_reference_only(self): - global _UID - entry = self.stack.content_type('faq').entry(_UID).only('field1') + entry = self.stack.content_type('faq').entry(FAQ_UID).only('field1') entry.fetch() self.assertEqual({'environment': 'development', 'only[BASE][]': 'field1'}, entry.entry_param) def test_11_entry_base_include_reference_excepts(self): - global _UID - entry = self.stack.content_type( - 'faq').entry(_UID).excepts('field1') + entry = self.stack.content_type('faq').entry(FAQ_UID).excepts('field1') entry.fetch() self.assertEqual({'environment': 'development', 'except[BASE][]': 'field1'}, entry.entry_param) @@ -95,15 +80,13 @@ def test_12_entry_include_reference_github_issue(self): response = _entry.fetch() def test_13_entry_support_include_fallback_unit_test(self): - global _UID - entry = self.stack.content_type('faq').entry( - _UID).include_fallback() + entry = self.stack.content_type('faq').entry(FAQ_UID).include_fallback() self.assertEqual( True, entry.entry_param.__contains__('include_fallback')) def test_14_entry_queryable_only(self): try: - entry = self.stack.content_type('faq').entry(_UID).only(4) + entry = self.stack.content_type('faq').entry(FAQ_UID).only(4) result = entry.fetch() self.assertEqual(None, result['uid']) except KeyError as e: @@ -112,7 +95,7 @@ def test_14_entry_queryable_only(self): def test_entry_queryable_excepts(self): try: - entry = self.stack.content_type('faq').entry(_UID).excepts(4) + entry = self.stack.content_type('faq').entry(FAQ_UID).excepts(4) result = entry.fetch() self.assertEqual(None, result['uid']) except KeyError as e: @@ -120,20 +103,17 @@ def test_entry_queryable_excepts(self): self.assertEqual("Invalid field_UID provided", e.args[0]) def test_16_entry_queryable_include_content_type(self): - entry = self.stack.content_type('faq').entry( - _UID).include_content_type() + entry = self.stack.content_type('faq').entry(FAQ_UID).include_content_type() self.assertEqual({'include_content_type': 'true', 'include_global_field_schema': 'true'}, entry.entry_queryable_param) def test_reference_content_type_uid(self): - entry = self.stack.content_type('faq').entry( - _UID).include_reference_content_type_uid() + entry = self.stack.content_type('faq').entry(FAQ_UID).include_reference_content_type_uid() self.assertEqual({'include_reference_content_type_uid': 'true'}, entry.entry_queryable_param) def test_19_entry_queryable_add_param(self): - entry = self.stack.content_type('faq').entry( - _UID).add_param('cms', 'contentstack') + entry = self.stack.content_type('faq').entry(FAQ_UID).add_param('cms', 'contentstack') self.assertEqual({'cms': 'contentstack'}, entry.entry_queryable_param) def test_20_entry_include_fallback(self): diff --git a/tests/test_live_preview.py b/tests/test_live_preview.py index e9bc0b5..485c55c 100644 --- a/tests/test_live_preview.py +++ b/tests/test_live_preview.py @@ -4,9 +4,9 @@ import contentstack from contentstack.deep_merge_lp import DeepMergeMixin -management_token = 'cs8743874323343u9' -entry_uid = 'blt8743874323343u9' -preview_token = 'abcdefgh1234567890' +management_token = config.MANAGEMENT_TOKEN +entry_uid = config.LIVE_PREVIEW_ENTRY_UID +preview_token = config.PREVIEW_TOKEN _lp_query = { 'live_preview': '#0#0#0#0#0#0#0#0#0#', diff --git a/tests/test_stack.py b/tests/test_stack.py index 205c3f7..886ecea 100644 --- a/tests/test_stack.py +++ b/tests/test_stack.py @@ -131,11 +131,14 @@ def test__15_sync_pagination_with_invalid_pagination_token(self): self.assertEqual( 'is not valid.', result['errors']['pagination_token'][0]) - @unittest.skip('Work in progress') - def test_16_initialise_sync(self): - result = self.stack.sync_init() - if result is not None: - self.assertEqual(16, result['total_count']) + # Deprecated: This test was skipped due to deprecation of the sync_init feature or its API. + # If sync_init is permanently removed or unsupported, this test should remain commented or be deleted. + # If migration or replacement is planned, update this test accordingly. + # @unittest.skip('Work in progress') + # def test_16_initialise_sync(self): + # result = self.stack.sync_init() + # if result is not None: + # self.assertEqual(16, result['total_count']) def test_17_entry_with_sync_token(self): result = self.stack.sync_token('sync_token') From b84cc12ad179bc39fdc99bfc9ab4e168efc97104 Mon Sep 17 00:00:00 2001 From: Aniket Shikhare <62753263+AniketDev7@users.noreply.github.com> Date: Fri, 4 Jul 2025 11:48:22 +0530 Subject: [PATCH 2/7] Upgrade requests to 2.32.4 and urllib3 to 2.5.0 to resolve Snyk vulnerabilities --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 19fc806..36a1196 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ twython==3.9.1 setuptools==80.3.1 contentstack-utils==1.3.0 python-dateutil==2.8.2 -requests==2.32.3 +requests==2.32.4 coverage==7.6.0 tox==4.5.1 virtualenv==20.26.6 @@ -58,6 +58,6 @@ zipp==3.20.1 distlib~=0.3.8 cachetools~=5.4.0 tomlkit~=0.13.2 -urllib3==2.2.3 +urllib3==2.5.0 exceptiongroup~=1.2.2 iniconfig~=2.0.0 \ No newline at end of file From e1e716cbc981af764cbd2bef8e73c892af4b0d6e Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Fri, 4 Jul 2025 11:59:05 +0530 Subject: [PATCH 3/7] Added version bump --- CHANGELOG.md | 6 ++++++ contentstack/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2124497..9be03fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## _v2.1.1_ + +### **Date: 07-July-2025** + +- Fixed sanity testcases and removed hardcoded secrets. + ## _v2.1.0_ ### **Date: 02-June-2025** diff --git a/contentstack/__init__.py b/contentstack/__init__.py index 74d26e0..c1603e4 100644 --- a/contentstack/__init__.py +++ b/contentstack/__init__.py @@ -22,7 +22,7 @@ __title__ = 'contentstack-delivery-python' __author__ = 'contentstack' __status__ = 'debug' -__version__ = 'v2.1.0' +__version__ = 'v2.1.1' __endpoint__ = 'cdn.contentstack.io' __email__ = 'support@contentstack.com' __developer_email__ = 'mobile@contentstack.com' From 5c2456dc383a9a3bffd970d278b86fe99e0609b0 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 9 Jul 2025 12:53:33 +0530 Subject: [PATCH 4/7] Added variants support in Python CDA SDK --- CHANGELOG.md | 6 ++++++ contentstack/__init__.py | 2 +- contentstack/contenttype.py | 20 ++++++++++++++++++++ contentstack/entry.py | 20 ++++++++++++++++++++ tests/test_entry.py | 24 +++++++++++++++++++++++- 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9be03fd..22b14f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## _v2.2.0_ + +### **Date: 14-July-2025** + +- Variants Support Added. + ## _v2.1.1_ ### **Date: 07-July-2025** diff --git a/contentstack/__init__.py b/contentstack/__init__.py index c1603e4..d8f02b0 100644 --- a/contentstack/__init__.py +++ b/contentstack/__init__.py @@ -22,7 +22,7 @@ __title__ = 'contentstack-delivery-python' __author__ = 'contentstack' __status__ = 'debug' -__version__ = 'v2.1.1' +__version__ = 'v2.2.0' __endpoint__ = 'cdn.contentstack.io' __email__ = 'support@contentstack.com' __developer_email__ = 'mobile@contentstack.com' diff --git a/contentstack/contenttype.py b/contentstack/contenttype.py index 363a206..88aca09 100644 --- a/contentstack/contenttype.py +++ b/contentstack/contenttype.py @@ -118,3 +118,23 @@ def find(self, params=None): url = f'{endpoint}/content_types?{encoded_params}' result = self.http_instance.get(url) return result + + def variants(self, variant_uid: str | list[str], params: dict = None): + """ + Fetches the variants of the content type + :param variant_uid: {str} -- variant_uid + :return: Entry, so you can chain this call. + """ + if isinstance(variant_uid, str): + self.http_instance.headers['x-cs-variant-uid'] = variant_uid + elif isinstance(variant_uid, list): + self.http_instance.headers['x-cs-variant-uid'] = ','.join(variant_uid) + + if params is not None: + self.local_param.update(params) + + encoded_params = parse.urlencode(self.local_param) + endpoint = self.http_instance.endpoint + url = f'{endpoint}/content_types/{self.__content_type_uid}/entries?{encoded_params}' + result = self.http_instance.get(url) + return result diff --git a/contentstack/entry.py b/contentstack/entry.py index 7cfcb13..ecb93e0 100644 --- a/contentstack/entry.py +++ b/contentstack/entry.py @@ -222,6 +222,26 @@ def _merged_response(self): 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") + + def variants(self, variant_uid: str | list[str], params: dict = None): + """ + Fetches the variants of the entry + :param variant_uid: {str} -- variant_uid + :return: Entry, so you can chain this call. + """ + if isinstance(variant_uid, str): + self.http_instance.headers['x-cs-variant-uid'] = variant_uid + elif isinstance(variant_uid, list): + self.http_instance.headers['x-cs-variant-uid'] = ','.join(variant_uid) + + if params is not None: + self.entry_param.update(params) + encoded_params = parse.urlencode(self.entry_param) + endpoint = self.http_instance.endpoint + url = f'{endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}?{encoded_params}' + result = self.http_instance.get(url) + return result + diff --git a/tests/test_entry.py b/tests/test_entry.py index fe30466..d00a1bc 100644 --- a/tests/test_entry.py +++ b/tests/test_entry.py @@ -8,7 +8,7 @@ ENVIRONMENT = config.ENVIRONMENT HOST = config.HOST FAQ_UID = config.FAQ_UID # Add this in your config.py - +VARIANT_UID = config.VARIANT_UID class TestEntry(unittest.TestCase): @@ -134,6 +134,28 @@ def test_22_entry_include_metadata(self): content_type = self.stack.content_type('faq') entry = content_type.entry("878783238783").include_metadata() self.assertEqual({'include_metadata': 'true'}, entry.entry_queryable_param) + + def test_23_content_type_variants(self): + content_type = self.stack.content_type('faq') + entry = content_type.variants(VARIANT_UID) + self.assertIn('variants', entry['entries'][0]['publish_details']) + + def test_24_entry_variants(self): + content_type = self.stack.content_type('faq') + entry = content_type.entry(FAQ_UID).variants(VARIANT_UID) + self.assertIn('variants', entry['entry']['publish_details']) + + def test_25_content_type_variants_with_has_hash_variant(self): + content_type = self.stack.content_type('faq') + entry = content_type.variants([VARIANT_UID]) + self.assertIn('variants', entry['entries'][0]['publish_details']) + + def test_25_content_type_entry_variants_with_has_hash_variant(self): + content_type = self.stack.content_type('faq').entry(FAQ_UID) + entry = content_type.variants([VARIANT_UID]) + self.assertIn('variants', entry['entry']['publish_details']) + + if __name__ == '__main__': From 4843091c20ee2c6b7a27af5625f393be159ec6a1 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 9 Jul 2025 13:03:19 +0530 Subject: [PATCH 5/7] Pass the modified headers explicitly --- contentstack/contenttype.py | 7 ++++--- contentstack/entry.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/contentstack/contenttype.py b/contentstack/contenttype.py index 88aca09..ab5b72d 100644 --- a/contentstack/contenttype.py +++ b/contentstack/contenttype.py @@ -125,10 +125,11 @@ def variants(self, variant_uid: str | list[str], params: dict = None): :param variant_uid: {str} -- variant_uid :return: Entry, so you can chain this call. """ + headers = self.http_instance.headers.copy() # Create a local copy of headers if isinstance(variant_uid, str): - self.http_instance.headers['x-cs-variant-uid'] = variant_uid + headers['x-cs-variant-uid'] = variant_uid elif isinstance(variant_uid, list): - self.http_instance.headers['x-cs-variant-uid'] = ','.join(variant_uid) + headers['x-cs-variant-uid'] = ','.join(variant_uid) if params is not None: self.local_param.update(params) @@ -136,5 +137,5 @@ def variants(self, variant_uid: str | list[str], params: dict = None): encoded_params = parse.urlencode(self.local_param) endpoint = self.http_instance.endpoint url = f'{endpoint}/content_types/{self.__content_type_uid}/entries?{encoded_params}' - result = self.http_instance.get(url) + result = self.http_instance.get(url, headers=headers) return result diff --git a/contentstack/entry.py b/contentstack/entry.py index ecb93e0..be7dd65 100644 --- a/contentstack/entry.py +++ b/contentstack/entry.py @@ -229,17 +229,18 @@ def variants(self, variant_uid: str | list[str], params: dict = None): :param variant_uid: {str} -- variant_uid :return: Entry, so you can chain this call. """ + headers = self.http_instance.headers.copy() # Create a local copy of headers if isinstance(variant_uid, str): - self.http_instance.headers['x-cs-variant-uid'] = variant_uid + headers['x-cs-variant-uid'] = variant_uid elif isinstance(variant_uid, list): - self.http_instance.headers['x-cs-variant-uid'] = ','.join(variant_uid) + headers['x-cs-variant-uid'] = ','.join(variant_uid) if params is not None: self.entry_param.update(params) encoded_params = parse.urlencode(self.entry_param) endpoint = self.http_instance.endpoint url = f'{endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}?{encoded_params}' - result = self.http_instance.get(url) + result = self.http_instance.get(url, headers=headers) return result From 5734a1f1e67ba3b50d6bdb5ae46aa65e328c2dfa Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 9 Jul 2025 16:17:42 +0530 Subject: [PATCH 6/7] Added find and fetch methods for variants --- contentstack/contenttype.py | 23 ++++----- contentstack/entry.py | 22 ++++----- contentstack/variants.py | 94 +++++++++++++++++++++++++++++++++++++ tests/test_entry.py | 8 ++-- 4 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 contentstack/variants.py diff --git a/contentstack/contenttype.py b/contentstack/contenttype.py index ab5b72d..2795e29 100644 --- a/contentstack/contenttype.py +++ b/contentstack/contenttype.py @@ -13,6 +13,7 @@ from contentstack.entry import Entry from contentstack.query import Query +from contentstack.variants import Variants class ContentType: """ @@ -125,17 +126,11 @@ def variants(self, variant_uid: str | list[str], params: dict = None): :param variant_uid: {str} -- variant_uid :return: Entry, so you can chain this call. """ - headers = self.http_instance.headers.copy() # Create a local copy of headers - if isinstance(variant_uid, str): - headers['x-cs-variant-uid'] = variant_uid - elif isinstance(variant_uid, list): - headers['x-cs-variant-uid'] = ','.join(variant_uid) - - if params is not None: - self.local_param.update(params) - - encoded_params = parse.urlencode(self.local_param) - endpoint = self.http_instance.endpoint - url = f'{endpoint}/content_types/{self.__content_type_uid}/entries?{encoded_params}' - result = self.http_instance.get(url, headers=headers) - return result + return Variants( + http_instance=self.http_instance, + content_type_uid=self.__content_type_uid, + entry_uid=None, + variant_uid=variant_uid, + params=params, + logger=None + ) diff --git a/contentstack/entry.py b/contentstack/entry.py index be7dd65..0c44190 100644 --- a/contentstack/entry.py +++ b/contentstack/entry.py @@ -8,6 +8,7 @@ from contentstack.deep_merge_lp import DeepMergeMixin from contentstack.entryqueryable import EntryQueryable +from contentstack.variants import Variants class Entry(EntryQueryable): """ @@ -229,19 +230,14 @@ def variants(self, variant_uid: str | list[str], params: dict = None): :param variant_uid: {str} -- variant_uid :return: Entry, so you can chain this call. """ - headers = self.http_instance.headers.copy() # Create a local copy of headers - if isinstance(variant_uid, str): - headers['x-cs-variant-uid'] = variant_uid - elif isinstance(variant_uid, list): - headers['x-cs-variant-uid'] = ','.join(variant_uid) - - if params is not None: - self.entry_param.update(params) - encoded_params = parse.urlencode(self.entry_param) - endpoint = self.http_instance.endpoint - url = f'{endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}?{encoded_params}' - result = self.http_instance.get(url, headers=headers) - return result + return Variants( + http_instance=self.http_instance, + content_type_uid=self.content_type_id, + entry_uid=self.entry_uid, + variant_uid=variant_uid, + params=params, + logger=None + ) diff --git a/contentstack/variants.py b/contentstack/variants.py new file mode 100644 index 0000000..2d16c8b --- /dev/null +++ b/contentstack/variants.py @@ -0,0 +1,94 @@ +import logging +from urllib import parse + +from contentstack.deep_merge_lp import DeepMergeMixin +from contentstack.entryqueryable import EntryQueryable + +class Variants(EntryQueryable, ): + """ + An entry is the actual piece of content that you want to publish. + Entries can be created for one of the available content types. + + Entry works with + version={version_number} + environment={environment_name} + locale={locale_code} + """ + + def __init__(self, + http_instance=None, + content_type_uid=None, + entry_uid=None, + variant_uid=None, + params=None, + logger=None): + + super().__init__() + EntryQueryable.__init__(self) + self.entry_param = {} + self.http_instance = http_instance + self.content_type_id = content_type_uid + self.entry_uid = entry_uid + self.variant_uid = variant_uid + self.logger = logger or logging.getLogger(__name__) + self.entry_param = params or {} + + def find(self, params=None): + """ + find the variants of the entry of a particular content type + :param self.variant_uid: {str} -- self.variant_uid + :return: Entry, so you can chain this call. + """ + headers = self.http_instance.headers.copy() # Create a local copy of headers + if isinstance(self.variant_uid, str): + headers['x-cs-variant-uid'] = self.variant_uid + elif isinstance(self.variant_uid, list): + headers['x-cs-variant-uid'] = ','.join(self.variant_uid) + + if params is not None: + self.entry_param.update(params) + encoded_params = parse.urlencode(self.entry_param) + endpoint = self.http_instance.endpoint + url = f'{endpoint}/content_types/{self.content_type_id}/entries?{encoded_params}' + self.http_instance.headers.update(headers) + result = self.http_instance.get(url) + self.http_instance.headers.pop('x-cs-variant-uid', None) + return result + + def fetch(self, params=None): + """ + This method is useful to fetch variant entries of a paticular content type and entries of the of the stack. + :return:dict -- contentType response + ------------------------------ + Example: + + >>> import contentstack + >>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment') + >>> content_type = stack.content_type('content_type_uid') + >>> some_dict = {'abc':'something'} + >>> response = content_type.fetch(some_dict) + ------------------------------ + """ + """ + Fetches the variants of the entry + :param self.variant_uid: {str} -- self.variant_uid + :return: Entry, so you can chain this call. + """ + if self.entry_uid is None: + raise ValueError("entry_uid is required") + else: + headers = self.http_instance.headers.copy() # Create a local copy of headers + if isinstance(self.variant_uid, str): + headers['x-cs-variant-uid'] = self.variant_uid + elif isinstance(self.variant_uid, list): + headers['x-cs-variant-uid'] = ','.join(self.variant_uid) + + if params is not None: + self.entry_param.update(params) + encoded_params = parse.urlencode(self.entry_param) + endpoint = self.http_instance.endpoint + url = f'{endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}?{encoded_params}' + self.http_instance.headers.update(headers) + result = self.http_instance.get(url) + self.http_instance.headers.pop('x-cs-variant-uid', None) + return result \ No newline at end of file diff --git a/tests/test_entry.py b/tests/test_entry.py index d00a1bc..cdfeb4d 100644 --- a/tests/test_entry.py +++ b/tests/test_entry.py @@ -137,22 +137,22 @@ def test_22_entry_include_metadata(self): def test_23_content_type_variants(self): content_type = self.stack.content_type('faq') - entry = content_type.variants(VARIANT_UID) + entry = content_type.variants(VARIANT_UID).find() self.assertIn('variants', entry['entries'][0]['publish_details']) def test_24_entry_variants(self): content_type = self.stack.content_type('faq') - entry = content_type.entry(FAQ_UID).variants(VARIANT_UID) + entry = content_type.entry(FAQ_UID).variants(VARIANT_UID).fetch() self.assertIn('variants', entry['entry']['publish_details']) def test_25_content_type_variants_with_has_hash_variant(self): content_type = self.stack.content_type('faq') - entry = content_type.variants([VARIANT_UID]) + entry = content_type.variants([VARIANT_UID]).find() self.assertIn('variants', entry['entries'][0]['publish_details']) def test_25_content_type_entry_variants_with_has_hash_variant(self): content_type = self.stack.content_type('faq').entry(FAQ_UID) - entry = content_type.variants([VARIANT_UID]) + entry = content_type.variants([VARIANT_UID]).fetch() self.assertIn('variants', entry['entry']['publish_details']) From c0e6400092113afbfecd4251fb703352a3b18279 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 9 Jul 2025 16:22:53 +0530 Subject: [PATCH 7/7] Fixed PR comments by copilot --- contentstack/entry.py | 2 +- contentstack/variants.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/contentstack/entry.py b/contentstack/entry.py index 0c44190..f2836f8 100644 --- a/contentstack/entry.py +++ b/contentstack/entry.py @@ -236,7 +236,7 @@ def variants(self, variant_uid: str | list[str], params: dict = None): entry_uid=self.entry_uid, variant_uid=variant_uid, params=params, - logger=None + logger=self.logger ) diff --git a/contentstack/variants.py b/contentstack/variants.py index 2d16c8b..133630d 100644 --- a/contentstack/variants.py +++ b/contentstack/variants.py @@ -1,10 +1,9 @@ import logging from urllib import parse -from contentstack.deep_merge_lp import DeepMergeMixin from contentstack.entryqueryable import EntryQueryable -class Variants(EntryQueryable, ): +class Variants(EntryQueryable): """ An entry is the actual piece of content that you want to publish. Entries can be created for one of the available content types. @@ -57,7 +56,7 @@ def find(self, params=None): def fetch(self, params=None): """ - This method is useful to fetch variant entries of a paticular content type and entries of the of the stack. + This method is useful to fetch variant entries of a particular content type and entries of the of the stack. :return:dict -- contentType response ------------------------------ Example: