Contentstack 2.2.0__tar.gz → 2.3.0__tar.gz

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.
Files changed (37) hide show
  1. {contentstack-2.2.0 → contentstack-2.3.0}/Contentstack.egg-info/PKG-INFO +1 -1
  2. {contentstack-2.2.0 → contentstack-2.3.0}/Contentstack.egg-info/SOURCES.txt +3 -1
  3. {contentstack-2.2.0 → contentstack-2.3.0}/PKG-INFO +1 -1
  4. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/__init__.py +1 -1
  5. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/stack.py +9 -0
  6. contentstack-2.3.0/contentstack/taxonomy.py +64 -0
  7. contentstack-2.3.0/tests/test_taxonomies.py +99 -0
  8. {contentstack-2.2.0 → contentstack-2.3.0}/Contentstack.egg-info/dependency_links.txt +0 -0
  9. {contentstack-2.2.0 → contentstack-2.3.0}/Contentstack.egg-info/not-zip-safe +0 -0
  10. {contentstack-2.2.0 → contentstack-2.3.0}/Contentstack.egg-info/requires.txt +0 -0
  11. {contentstack-2.2.0 → contentstack-2.3.0}/Contentstack.egg-info/top_level.txt +0 -0
  12. {contentstack-2.2.0 → contentstack-2.3.0}/LICENSE +0 -0
  13. {contentstack-2.2.0 → contentstack-2.3.0}/README.md +0 -0
  14. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/asset.py +0 -0
  15. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/assetquery.py +0 -0
  16. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/basequery.py +0 -0
  17. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/contenttype.py +0 -0
  18. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/controller.py +0 -0
  19. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/deep_merge_lp.py +0 -0
  20. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/entry.py +0 -0
  21. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/entryqueryable.py +0 -0
  22. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/globalfields.py +0 -0
  23. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/https_connection.py +0 -0
  24. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/image_transform.py +0 -0
  25. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/query.py +0 -0
  26. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/utility.py +0 -0
  27. {contentstack-2.2.0 → contentstack-2.3.0}/contentstack/variants.py +0 -0
  28. {contentstack-2.2.0 → contentstack-2.3.0}/setup.cfg +0 -0
  29. {contentstack-2.2.0 → contentstack-2.3.0}/setup.py +0 -0
  30. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_assets.py +0 -0
  31. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_early_fetch.py +0 -0
  32. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_early_find.py +0 -0
  33. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_entry.py +0 -0
  34. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_global_fields.py +0 -0
  35. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_live_preview.py +0 -0
  36. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_query.py +0 -0
  37. {contentstack-2.2.0 → contentstack-2.3.0}/tests/test_stack.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Contentstack
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Contentstack is a headless CMS with an API-first approach.
5
5
  Home-page: https://github.com/contentstack/contentstack-python
6
6
  Author: Contentstack
@@ -21,6 +21,7 @@ contentstack/https_connection.py
21
21
  contentstack/image_transform.py
22
22
  contentstack/query.py
23
23
  contentstack/stack.py
24
+ contentstack/taxonomy.py
24
25
  contentstack/utility.py
25
26
  contentstack/variants.py
26
27
  tests/test_assets.py
@@ -30,4 +31,5 @@ tests/test_entry.py
30
31
  tests/test_global_fields.py
31
32
  tests/test_live_preview.py
32
33
  tests/test_query.py
33
- tests/test_stack.py
34
+ tests/test_stack.py
35
+ tests/test_taxonomies.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Contentstack
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Contentstack is a headless CMS with an API-first approach.
5
5
  Home-page: https://github.com/contentstack/contentstack-python
6
6
  Author: Contentstack
@@ -22,7 +22,7 @@ __all__ = (
22
22
  __title__ = 'contentstack-delivery-python'
23
23
  __author__ = 'contentstack'
24
24
  __status__ = 'debug'
25
- __version__ = 'v2.2.0'
25
+ __version__ = 'v2.3.0'
26
26
  __endpoint__ = 'cdn.contentstack.io'
27
27
  __email__ = 'support@contentstack.com'
28
28
  __developer_email__ = 'mobile@contentstack.com'
@@ -6,6 +6,7 @@ from urllib3.util import Retry
6
6
  from contentstack.asset import Asset
7
7
  from contentstack.assetquery import AssetQuery
8
8
  from contentstack.contenttype import ContentType
9
+ from contentstack.taxonomy import Taxonomy
9
10
  from contentstack.globalfields import GlobalField
10
11
  from contentstack.https_connection import HTTPSConnection
11
12
  from contentstack.image_transform import ImageTransform
@@ -204,6 +205,14 @@ class Stack:
204
205
  """
205
206
  return ContentType(self.http_instance, content_type_uid)
206
207
 
208
+ def taxonomy(self):
209
+ """
210
+ taxonomy defines the structure or schema of a page or a section
211
+ of your web or mobile property.
212
+ :return: taxonomy
213
+ """
214
+ return Taxonomy(self.http_instance)
215
+
207
216
  def global_field(self, global_field_uid=None):
208
217
  """
209
218
  Global field defines the structure or schema of a page or a section
@@ -0,0 +1,64 @@
1
+ import json
2
+ from urllib import parse
3
+ from urllib.parse import quote
4
+
5
+
6
+
7
+ class Taxonomy:
8
+ def __init__(self, http_instance):
9
+ self.http_instance = http_instance
10
+ self._filters: dict = {}
11
+
12
+ def _add(self, field: str, condition: dict) -> "TaxonomyQuery":
13
+ self._filters[field] = condition
14
+ return self
15
+
16
+ def in_(self, field: str, terms: list) -> "TaxonomyQuery":
17
+ return self._add(field, {"$in": terms})
18
+
19
+ def or_(self, *conds: dict) -> "TaxonomyQuery":
20
+ return self._add("$or", list(conds))
21
+
22
+ def and_(self, *conds: dict) -> "TaxonomyQuery":
23
+ return self._add("$and", list(conds))
24
+
25
+ def exists(self, field: str) -> "TaxonomyQuery":
26
+ return self._add(field, {"$exists": True})
27
+
28
+ def equal_and_below(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyQuery":
29
+ cond = {"$eq_below": term_uid, "levels": levels}
30
+ return self._add(field, cond)
31
+
32
+ def below(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyQuery":
33
+ cond = {"$below": term_uid, "levels": levels}
34
+ return self._add(field, cond)
35
+
36
+ def equal_and_above(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyQuery":
37
+ cond = {"$eq_above": term_uid, "levels": levels}
38
+ return self._add(field, cond)
39
+
40
+ def above(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyQuery":
41
+ cond = {"$above": term_uid, "levels": levels}
42
+ return self._add(field, cond)
43
+
44
+ def find(self, params=None):
45
+ """
46
+ This method fetches entries filtered by taxonomy from the stack.
47
+ """
48
+ self.local_param = {}
49
+ self.local_param['environment'] = self.http_instance.headers['environment']
50
+
51
+ # Ensure query param is always present
52
+ query_string = json.dumps(self._filters or {})
53
+ query_encoded = quote(query_string, safe='{}":,[]') # preserves JSON characters
54
+
55
+ # Build the base URL
56
+ endpoint = self.http_instance.endpoint
57
+ url = f'{endpoint}/taxonomies/entries?environment={self.local_param["environment"]}&query={query_encoded}'
58
+
59
+ # Append any additional params manually
60
+ if params:
61
+ other_params = '&'.join(f'{k}={v}' for k, v in params.items())
62
+ url += f'&{other_params}'
63
+ return self.http_instance.get(url)
64
+
@@ -0,0 +1,99 @@
1
+ import logging
2
+ import unittest
3
+ import config
4
+ import contentstack
5
+ import pytest
6
+
7
+ API_KEY = config.APIKEY
8
+ DELIVERY_TOKEN = config.DELIVERYTOKEN
9
+ ENVIRONMENT = config.ENVIRONMENT
10
+ HOST = config.HOST
11
+
12
+ class TestTaxonomyAPI(unittest.TestCase):
13
+ def setUp(self):
14
+ self.stack = contentstack.Stack(API_KEY, DELIVERY_TOKEN, ENVIRONMENT, host=HOST)
15
+
16
+ def test_01_taxonomy_complex_query(self):
17
+ """Test complex taxonomy query combining multiple filters"""
18
+ taxonomy = self.stack.taxonomy()
19
+ result = taxonomy.and_(
20
+ {"taxonomies.category": {"$in": ["test"]}},
21
+ {"taxonomies.test1": {"$exists": True}}
22
+ ).or_(
23
+ {"taxonomies.status": {"$in": ["active"]}},
24
+ {"taxonomies.priority": {"$in": ["high"]}}
25
+ ).find({'limit': 10})
26
+ if result is not None:
27
+ self.assertIn('entries', result)
28
+
29
+ def test_02_taxonomy_in_query(self):
30
+ """Test taxonomy query with $in filter"""
31
+ taxonomy = self.stack.taxonomy()
32
+ result = taxonomy.in_("taxonomies.category", ["category1", "category2"]).find()
33
+ if result is not None:
34
+ self.assertIn('entries', result)
35
+
36
+ def test_03_taxonomy_exists_query(self):
37
+ """Test taxonomy query with $exists filter"""
38
+ taxonomy = self.stack.taxonomy()
39
+ result = taxonomy.exists("taxonomies.test1").find()
40
+ if result is not None:
41
+ self.assertIn('entries', result)
42
+
43
+ def test_04_taxonomy_or_query(self):
44
+ """Test taxonomy query with $or filter"""
45
+ taxonomy = self.stack.taxonomy()
46
+ result = taxonomy.or_(
47
+ {"taxonomies.category": {"$in": ["category1"]}},
48
+ {"taxonomies.test1": {"$exists": True}}
49
+ ).find()
50
+ if result is not None:
51
+ self.assertIn('entries', result)
52
+
53
+ def test_05_taxonomy_and_query(self):
54
+ """Test taxonomy query with $and filter"""
55
+ taxonomy = self.stack.taxonomy()
56
+ result = taxonomy.and_(
57
+ {"taxonomies.category": {"$in": ["category1"]}},
58
+ {"taxonomies.test1": {"$exists": True}}
59
+ ).find()
60
+ if result is not None:
61
+ self.assertIn('entries', result)
62
+
63
+ def test_06_taxonomy_equal_and_below(self):
64
+ """Test taxonomy query with $eq_below filter"""
65
+ taxonomy = self.stack.taxonomy()
66
+ result = taxonomy.equal_and_below("taxonomies.color", "blue", levels=1).find()
67
+ if result is not None:
68
+ self.assertIn('entries', result)
69
+
70
+ def test_07_taxonomy_below(self):
71
+ """Test taxonomy query with $below filter"""
72
+ taxonomy = self.stack.taxonomy()
73
+ result = taxonomy.below("taxonomies.hierarchy", "parent_uid", levels=2).find()
74
+ if result is not None:
75
+ self.assertIn('entries', result)
76
+
77
+ def test_08_taxonomy_equal_and_above(self):
78
+ """Test taxonomy query with $eq_above filter"""
79
+ taxonomy = self.stack.taxonomy()
80
+ result = taxonomy.equal_and_above("taxonomies.hierarchy", "child_uid", levels=3).find()
81
+ if result is not None:
82
+ self.assertIn('entries', result)
83
+
84
+ def test_09_taxonomy_above(self):
85
+ """Test taxonomy query with $above filter"""
86
+ taxonomy = self.stack.taxonomy()
87
+ result = taxonomy.above("taxonomies.hierarchy", "child_uid", levels=2).find()
88
+ if result is not None:
89
+ self.assertIn('entries', result)
90
+
91
+ def test_10_taxonomy_find_with_params(self):
92
+ """Test taxonomy find with additional parameters"""
93
+ taxonomy = self.stack.taxonomy()
94
+ result = taxonomy.in_("taxonomies.category", ["test"]).find({'limit': 5})
95
+ if result is not None:
96
+ self.assertIn('entries', result)
97
+
98
+ if __name__ == '__main__':
99
+ unittest.main()
File without changes
File without changes
File without changes
File without changes