From e8cb6d113cba4fd8a2323cf4b2004e1ec0bb90d5 Mon Sep 17 00:00:00 2001 From: OMpawar-21 Date: Tue, 4 Aug 2026 12:15:30 +0530 Subject: [PATCH] feat(DX-9677): add taxonomy publish/unpublish/localize/unlocalize and term localize/unlocalize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds six new methods to the Python management SDK taxonomy/terms surfaces: - Taxonomy.publish() / unpublish() — POST /taxonomies/publish|unpublish (collection-level) - Taxonomy.localize() / unlocalize() — POST|DELETE /taxonomies/{uid}?locale=... - Terms.localize() / unlocalize() — POST|DELETE /taxonomies/{uid}/terms/{uid}?locale=... Unit tests (21 pass) and integration tests included. Locale passed via add_param("locale", "...") following existing SDK convention. Co-Authored-By: Claude Sonnet 4.6 --- .../taxonomies/taxonomy.py | 84 ++++++++++++++++++- contentstack_management/terms/terms.py | 44 +++++++++- tests/integration/api/test_07_taxonomy.py | 39 +++++++++ tests/integration/api/test_08_terms.py | 17 ++++ tests/unit/taxonomies/test_taxonomy_unit.py | 38 +++++++++ tests/unit/terms/test_terms_unit.py | 17 ++++ 6 files changed, 236 insertions(+), 3 deletions(-) diff --git a/contentstack_management/taxonomies/taxonomy.py b/contentstack_management/taxonomies/taxonomy.py index 1223a28..2058063 100644 --- a/contentstack_management/taxonomies/taxonomy.py +++ b/contentstack_management/taxonomies/taxonomy.py @@ -144,10 +144,92 @@ def delete(self, taxonomy_uid: str = None): return self.client.delete(url, headers = self.client.headers, params = self.params) + def publish(self, data: dict): + """ + Publish one or more taxonomies to the given environments and locales. + Call on a collection-level instance: stack.taxonomy().publish(...) + :param data: Request body with keys: locales, environments, items, scheduled_at. + + ------------------------------- + [Example:] + >>> data = { + >>> "locales": ["en-us"], + >>> "environments": ["production"], + >>> "items": [{"uid": "taxonomy_uid"}] + >>> } + >>> import contentstack_management + >>> client = contentstack_management.Client(authtoken='your_authtoken') + >>> result = client.stack('api_key').taxonomy().publish(data).json() + + ------------------------------- + """ + data = json.dumps(data) + return self.client.post(f"{self.path}/publish", headers=self.client.headers, data=data, params=self.params) + + def unpublish(self, data: dict): + """ + Unpublish one or more taxonomies from the given environments and locales. + Call on a collection-level instance: stack.taxonomy().unpublish(...) + :param data: Request body with keys: locales, environments, items, scheduled_at. + + ------------------------------- + [Example:] + >>> data = { + >>> "locales": ["en-us"], + >>> "environments": ["production"], + >>> "items": [{"uid": "taxonomy_uid"}] + >>> } + >>> import contentstack_management + >>> client = contentstack_management.Client(authtoken='your_authtoken') + >>> result = client.stack('api_key').taxonomy().unpublish(data).json() + + ------------------------------- + """ + data = json.dumps(data) + return self.client.post(f"{self.path}/unpublish", headers=self.client.headers, data=data, params=self.params) + + def localize(self, data: dict): + """ + Localize a taxonomy into the locale specified via add_param("locale", "..."). + :param data: Request body, e.g. {"taxonomy": {"name": "Localized name"}}. + + ------------------------------- + [Example:] + >>> import contentstack_management + >>> client = contentstack_management.Client(authtoken='your_authtoken') + >>> tax = client.stack('api_key').taxonomy('taxonomy_uid') + >>> tax.add_param("locale", "hi-in") + >>> result = tax.localize({"taxonomy": {"name": "Taxonomy HI"}}).json() + + ------------------------------- + """ + self.validate_taxonomy_uid() + url = f"{self.path}/{self.taxonomy_uid}" + data = json.dumps(data) + return self.client.post(url, headers=self.client.headers, data=data, params=self.params) + + def unlocalize(self): + """ + Unlocalize a taxonomy from the locale specified via add_param("locale", "..."). + + ------------------------------- + [Example:] + >>> import contentstack_management + >>> client = contentstack_management.Client(authtoken='your_authtoken') + >>> tax = client.stack('api_key').taxonomy('taxonomy_uid') + >>> tax.add_param("locale", "hi-in") + >>> result = tax.unlocalize().json() + + ------------------------------- + """ + self.validate_taxonomy_uid() + url = f"{self.path}/{self.taxonomy_uid}" + return self.client.delete(url, headers=self.client.headers, params=self.params) + def validate_taxonomy_uid(self): if self.taxonomy_uid is None or '': raise ArgumentException(TAXONOMY_UID_REQUIRED) - + def terms(self, terms_uid: str = None): self.validate_taxonomy_uid() return Terms(self.client, self.taxonomy_uid, terms_uid) diff --git a/contentstack_management/terms/terms.py b/contentstack_management/terms/terms.py index 18d5de1..b54e553 100644 --- a/contentstack_management/terms/terms.py +++ b/contentstack_management/terms/terms.py @@ -282,14 +282,54 @@ def descendants(self, terms_uid: str = None): url = f"{self.path}/{self.terms_uid}/descendants" return self.client.get(url, headers = self.client.headers, params = self.params) + def localize(self, data: dict): + """ + Localize a term into the locale specified via add_param("locale", "..."). + :param data: Request body, e.g. {"term": {"uid": "...", "name": "..."}}. + + ------------------------------- + [Example:] + >>> import contentstack_management + >>> client = contentstack_management.Client(authtoken='your_authtoken') + >>> term = client.stack('api_key').taxonomy('taxonomy_uid').terms('term_uid') + >>> term.add_param("locale", "hi-in") + >>> result = term.localize({"term": {"uid": "term_uid", "name": "Term HI"}}).json() + + ------------------------------- + """ + self.validate_taxonomy_uid() + self.validate_terms_uid() + url = f"{self.path}/{self.terms_uid}" + data = json.dumps(data) + return self.client.post(url, headers=self.client.headers, data=data, params=self.params) + + def unlocalize(self): + """ + Unlocalize a term from the locale specified via add_param("locale", "..."). + + ------------------------------- + [Example:] + >>> import contentstack_management + >>> client = contentstack_management.Client(authtoken='your_authtoken') + >>> term = client.stack('api_key').taxonomy('taxonomy_uid').terms('term_uid') + >>> term.add_param("locale", "hi-in") + >>> result = term.unlocalize().json() + + ------------------------------- + """ + self.validate_taxonomy_uid() + self.validate_terms_uid() + url = f"{self.path}/{self.terms_uid}" + return self.client.delete(url, headers=self.client.headers, params=self.params) + def validate_taxonomy_uid(self): if self.taxonomy_uid is None or '': raise ArgumentException(TAXONOMY_UID_REQUIRED) - + def validate_terms_uid(self): if self.terms_uid is None or '': raise ArgumentException(TERMS_UID_REQUIRED) - + def validate_term_string(self, term_string): if term_string is None or '': raise ArgumentException(TERM_STRING_REQUIRED) diff --git a/tests/integration/api/test_07_taxonomy.py b/tests/integration/api/test_07_taxonomy.py index 7314f11..68e1957 100644 --- a/tests/integration/api/test_07_taxonomy.py +++ b/tests/integration/api/test_07_taxonomy.py @@ -39,6 +39,45 @@ def test_fetch_nonexistent(self, stack): h.assert_status(resp, 404, 422) +class TestTaxonomyPublish: + def test_publish(self, stack, store): + uid = store["taxonomies"]["main"] + data = { + "locales": ["en-us"], + "environments": ["development"], + "items": [{"uid": uid}] + } + resp = stack.taxonomy().publish(data) + h.assert_status(resp, 200, 202) + + def test_unpublish(self, stack, store): + uid = store["taxonomies"]["main"] + data = { + "locales": ["en-us"], + "environments": ["development"], + "items": [{"uid": uid}] + } + resp = stack.taxonomy().unpublish(data) + h.assert_status(resp, 200, 202) + + +class TestTaxonomyLocalize: + def test_localize(self, stack, store): + uid = store["taxonomies"]["main"] + tax = stack.taxonomy(uid) + tax.add_param("locale", "hi-in") + resp = tax.localize({"taxonomy": {"name": f"Localized {uid}"}}) + h.assert_status(resp, 200, 201) + + def test_unlocalize(self, stack, store): + uid = store["taxonomies"]["main"] + tax = stack.taxonomy(uid) + tax.add_param("locale", "hi-in") + stack.client.headers.pop("Content-Type", None) + resp = tax.unlocalize() + h.assert_status(resp, 200, 204) + + class TestTaxonomyDelete: def test_delete(self, stack): uid = h.generate_valid_uid("tax_del") diff --git a/tests/integration/api/test_08_terms.py b/tests/integration/api/test_08_terms.py index ca067ad..a8f77b6 100644 --- a/tests/integration/api/test_08_terms.py +++ b/tests/integration/api/test_08_terms.py @@ -78,6 +78,23 @@ def test_fetch_nonexistent(self, stack, taxonomy_uid): h.assert_status(resp, 404, 422) +class TestTermsLocalize: + def test_localize(self, stack, store, taxonomy_uid): + uid = store["terms"]["main"] + term = stack.taxonomy(taxonomy_uid).terms(uid) + term.add_param("locale", "hi-in") + resp = term.localize({"term": {"uid": uid, "name": f"Localized {uid}"}}) + h.assert_status(resp, 200, 201) + + def test_unlocalize(self, stack, store, taxonomy_uid): + uid = store["terms"]["main"] + term = stack.taxonomy(taxonomy_uid).terms(uid) + term.add_param("locale", "hi-in") + stack.client.headers.pop("Content-Type", None) + resp = term.unlocalize() + h.assert_status(resp, 200, 204) + + class TestTermsDelete: def test_delete(self, stack, taxonomy_uid): uid = h.generate_valid_uid("term_del") diff --git a/tests/unit/taxonomies/test_taxonomy_unit.py b/tests/unit/taxonomies/test_taxonomy_unit.py index 561ca88..2808940 100644 --- a/tests/unit/taxonomies/test_taxonomy_unit.py +++ b/tests/unit/taxonomies/test_taxonomy_unit.py @@ -71,3 +71,41 @@ def test_delete_taxonomy(self): response = self.client.stack(api_key).taxonomy(taxonomy_uid).delete() self.assertEqual(response.request.url, f"{self.client.endpoint}taxonomies/{taxonomy_uid}") self.assertEqual(response.request.method, "DELETE") + + def test_publish_taxonomy(self): + data = { + "locales": ["en-us"], + "environments": ["production"], + "items": [{"uid": taxonomy_uid}] + } + response = self.client.stack(api_key).taxonomy().publish(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}taxonomies/publish") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + def test_unpublish_taxonomy(self): + data = { + "locales": ["en-us"], + "environments": ["production"], + "items": [{"uid": taxonomy_uid}] + } + response = self.client.stack(api_key).taxonomy().unpublish(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}taxonomies/unpublish") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + def test_localize_taxonomy(self): + data = {"taxonomy": {"name": "Taxonomy HI"}} + tax = self.client.stack(api_key).taxonomy(taxonomy_uid) + tax.add_param("locale", "hi-in") + response = tax.localize(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}taxonomies/{taxonomy_uid}?locale=hi-in") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + def test_unlocalize_taxonomy(self): + tax = self.client.stack(api_key).taxonomy(taxonomy_uid) + tax.add_param("locale", "hi-in") + response = tax.unlocalize() + self.assertEqual(response.request.url, f"{self.client.endpoint}taxonomies/{taxonomy_uid}?locale=hi-in") + self.assertEqual(response.request.method, "DELETE") diff --git a/tests/unit/terms/test_terms_unit.py b/tests/unit/terms/test_terms_unit.py index 95ff992..aa2a8b0 100644 --- a/tests/unit/terms/test_terms_unit.py +++ b/tests/unit/terms/test_terms_unit.py @@ -95,5 +95,22 @@ def test_descendants(self): self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json") + def test_localize_term(self): + data = {"term": {"uid": terms_uid, "name": "Term HI"}} + term = self.client.stack(api_key).taxonomy(taxonomy_uid).terms(terms_uid) + term.add_param("locale", "hi-in") + response = term.localize(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}taxonomies/{taxonomy_uid}/terms/{terms_uid}?locale=hi-in") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + def test_unlocalize_term(self): + term = self.client.stack(api_key).taxonomy(taxonomy_uid).terms(terms_uid) + term.add_param("locale", "hi-in") + response = term.unlocalize() + self.assertEqual(response.request.url, f"{self.client.endpoint}taxonomies/{taxonomy_uid}/terms/{terms_uid}?locale=hi-in") + self.assertEqual(response.request.method, "DELETE") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + if __name__ == '__main__': unittest.main()