contentstack-management 1.8.0__tar.gz → 1.10.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.
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/PKG-INFO +3 -3
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/__init__.py +22 -2
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/contentstack.py +18 -8
- contentstack_management-1.10.0/contentstack_management/endpoint.py +180 -0
- contentstack_management-1.10.0/contentstack_management/region_refresh.py +81 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/PKG-INFO +3 -3
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/SOURCES.txt +2 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/requires.txt +2 -2
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/setup.py +19 -3
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/LICENSE +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/README.md +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/_api_client.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/_constant.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/_errors.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/_messages.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/aliases/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/aliases/aliases.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/assets/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/assets/assets.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/auditlogs/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/auditlogs/auditlog.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/branches/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/branches/branches.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/bulk_operations/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/bulk_operations/bulk_operation.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/common.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/content_types/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/content_types/content_type.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/delivery_token/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/delivery_token/delivery_token.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/entries/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/entries/entry.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/entry_variants/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/entry_variants/entry_variants.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/environments/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/environments/environment.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/extensions/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/extensions/extension.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/global_fields/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/global_fields/global_fields.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/labels/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/labels/label.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/locale/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/locale/locale.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/management_token/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/management_token/management_token.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/metadata/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/metadata/metadata.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/oauth/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/oauth/oauth_handler.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/oauth/oauth_interceptor.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/organizations/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/organizations/organization.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/publish_queue/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/publish_queue/publish_queue.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/release_items/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/release_items/release_item.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/releases/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/releases/release.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/roles/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/roles/roles.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/stack/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/stack/stack.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/taxonomies/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/taxonomies/taxonomy.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/terms/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/terms/terms.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/user_session/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/user_session/user_session.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/users/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/users/user.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/variant_group/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/variant_group/variant_group.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/variants/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/variants/variants.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/webhooks/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/webhooks/webhook.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/workflows/__init__.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/workflows/workflows.py +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/dependency_links.txt +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/top_level.txt +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/setup.cfg +0 -0
- {contentstack_management-1.8.0 → contentstack_management-1.10.0}/tests/test_contentstack.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentstack-management
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.0
|
|
4
4
|
Summary: Contentstack API Client Library for Python
|
|
5
5
|
Home-page: https://github.com/contentstack/contentstack-management-python
|
|
6
6
|
Author: dev-ex
|
|
@@ -12,13 +12,13 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Requires-Python: >=3.9
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: bson>=0.5.9
|
|
16
15
|
Requires-Dist: requests>=2.5.4
|
|
17
16
|
Requires-Dist: requests-toolbelt>=0.3.1
|
|
18
|
-
Requires-Dist: urllib3
|
|
17
|
+
Requires-Dist: urllib3<3.0.0,>=2.7.0
|
|
19
18
|
Provides-Extra: dev
|
|
20
19
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
21
20
|
Requires-Dist: twine>=4.0.2; extra == "dev"
|
|
21
|
+
Requires-Dist: packaging>=24.0; extra == "dev"
|
|
22
22
|
Requires-Dist: dotenv>=0.0.5; extra == "dev"
|
|
23
23
|
Dynamic: author
|
|
24
24
|
Dynamic: author-email
|
{contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/__init__.py
RENAMED
|
@@ -18,6 +18,7 @@ from .environments.environment import Environment
|
|
|
18
18
|
from .entries.entry import Entry
|
|
19
19
|
from .entry_variants.entry_variants import EntryVariants
|
|
20
20
|
from .contentstack import Client, Region
|
|
21
|
+
from .endpoint import Endpoint
|
|
21
22
|
from ._api_client import _APIClient
|
|
22
23
|
from .common import Parameter
|
|
23
24
|
from ._errors import ArgumentException
|
|
@@ -36,11 +37,13 @@ from .variant_group.variant_group import VariantGroup
|
|
|
36
37
|
from .variants.variants import Variants
|
|
37
38
|
from .oauth.oauth_handler import OAuthHandler
|
|
38
39
|
from .oauth.oauth_interceptor import OAuthInterceptor
|
|
40
|
+
from .region_refresh import refresh_regions
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
__all__ = (
|
|
42
44
|
"Client",
|
|
43
45
|
"Region",
|
|
46
|
+
"Endpoint",
|
|
44
47
|
"_APIClient",
|
|
45
48
|
"Parameter",
|
|
46
49
|
"ArgumentException",
|
|
@@ -75,14 +78,31 @@ __all__ = (
|
|
|
75
78
|
"VariantGroup",
|
|
76
79
|
"Variants",
|
|
77
80
|
"OAuthHandler",
|
|
78
|
-
"OAuthInterceptor"
|
|
81
|
+
"OAuthInterceptor",
|
|
82
|
+
"refresh_regions",
|
|
79
83
|
)
|
|
80
84
|
|
|
85
|
+
def get_contentstack_endpoint(region='us', service='', omit_https=False):
|
|
86
|
+
"""
|
|
87
|
+
Resolve a Contentstack service endpoint URL for a given region.
|
|
88
|
+
|
|
89
|
+
Proxy to :class:`Endpoint.get_contentstack_endpoint` for convenience —
|
|
90
|
+
mirrors ``Contentstack::getContentstackEndpoint()`` in the PHP SDK.
|
|
91
|
+
|
|
92
|
+
:param region: Region ID or alias ('us', 'eu', 'azure-na', 'gcp-eu', ...).
|
|
93
|
+
:param service: Service key ('contentDelivery', 'contentManagement', ...).
|
|
94
|
+
When empty, returns a dict of all endpoints for the region.
|
|
95
|
+
:param omit_https: When True, strips 'https://' from the returned URL(s).
|
|
96
|
+
:returns: str when service is provided, dict[str,str] otherwise.
|
|
97
|
+
"""
|
|
98
|
+
return Endpoint.get_contentstack_endpoint(region, service, omit_https)
|
|
99
|
+
|
|
100
|
+
|
|
81
101
|
__title__ = 'contentstack-management-python'
|
|
82
102
|
__author__ = 'dev-ex'
|
|
83
103
|
__status__ = 'debug'
|
|
84
104
|
__region__ = 'na'
|
|
85
|
-
__version__ = '1.
|
|
105
|
+
__version__ = '1.10.0'
|
|
86
106
|
__host__ = 'api.contentstack.io'
|
|
87
107
|
__protocol__ = 'https://'
|
|
88
108
|
__api_version__ = 'v3'
|
|
@@ -2,6 +2,7 @@ from enum import Enum
|
|
|
2
2
|
import os
|
|
3
3
|
import pyotp
|
|
4
4
|
from ._api_client import _APIClient
|
|
5
|
+
from .endpoint import Endpoint
|
|
5
6
|
from contentstack_management.organizations import organization
|
|
6
7
|
from contentstack_management.stack import stack
|
|
7
8
|
from contentstack_management.user_session import user_session
|
|
@@ -37,16 +38,25 @@ class Client:
|
|
|
37
38
|
authtoken: str = None , management_token=None, headers: dict = None,
|
|
38
39
|
region: Region = Region.US.value, version='v3', timeout=2, max_retries: int = 18, early_access: list = None,
|
|
39
40
|
oauth_config: dict = None, **kwargs):
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
_DEFAULT_HOST = 'api.contentstack.io'
|
|
42
|
+
self.endpoint = f'{scheme}{_DEFAULT_HOST}/{version}/'
|
|
43
|
+
|
|
44
|
+
if host is None or host == _DEFAULT_HOST:
|
|
45
|
+
# No custom host — resolve via Endpoint (regions.json-driven)
|
|
46
|
+
try:
|
|
47
|
+
base = Endpoint.get_contentstack_endpoint(
|
|
48
|
+
region or 'us', 'contentManagement', omit_https=True)
|
|
49
|
+
self.endpoint = f'{scheme}{base}/{version}/'
|
|
50
|
+
except (ValueError, RuntimeError):
|
|
51
|
+
# Unknown/custom region string — fall back to legacy pattern
|
|
52
|
+
if region and region != Region.US.value:
|
|
53
|
+
self.endpoint = f'{scheme}{region}-api.contentstack.com/{version}/'
|
|
54
|
+
else:
|
|
55
|
+
# Explicit custom host always wins; apply region prefix when non-US
|
|
56
|
+
if region and region != Region.US.value:
|
|
44
57
|
self.endpoint = f'{scheme}{region}-api.{host}/{version}/'
|
|
45
58
|
else:
|
|
46
|
-
|
|
47
|
-
self.endpoint = f'{scheme}{region}-{host}/{version}/'
|
|
48
|
-
elif host is not None and host != 'api.contentstack.io':
|
|
49
|
-
self.endpoint = f'{scheme}{host}/{version}/'
|
|
59
|
+
self.endpoint = f'{scheme}{host}/{version}/'
|
|
50
60
|
if headers is None:
|
|
51
61
|
headers = {}
|
|
52
62
|
if early_access is not None:
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Endpoint — Contentstack region-to-URL resolver for the Management SDK.
|
|
3
|
+
|
|
4
|
+
Resolves Contentstack service endpoint URLs for any supported region.
|
|
5
|
+
Region data is loaded from contentstack_management/data/regions.json (bundled)
|
|
6
|
+
and cached in-memory for the lifetime of the process. When the bundled file is
|
|
7
|
+
absent the class attempts a live download from the Contentstack CDN so the
|
|
8
|
+
SDK continues to work even when the file was not created during installation.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
REGIONS_URL = 'https://artifacts.contentstack.com/regions.json'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Endpoint:
|
|
19
|
+
"""
|
|
20
|
+
Resolves Contentstack service endpoint URLs for any supported region.
|
|
21
|
+
|
|
22
|
+
Usage::
|
|
23
|
+
|
|
24
|
+
from contentstack_management.endpoint import Endpoint
|
|
25
|
+
|
|
26
|
+
# Single service URL
|
|
27
|
+
url = Endpoint.get_contentstack_endpoint('eu', 'contentManagement')
|
|
28
|
+
# 'https://eu-api.contentstack.com'
|
|
29
|
+
|
|
30
|
+
# All services for a region
|
|
31
|
+
endpoints = Endpoint.get_contentstack_endpoint('azure-na')
|
|
32
|
+
# {'contentDelivery': '...', 'contentManagement': '...', ...}
|
|
33
|
+
|
|
34
|
+
# Strip scheme (useful when building endpoint strings manually)
|
|
35
|
+
host = Endpoint.get_contentstack_endpoint('gcp-eu', 'contentManagement', omit_https=True)
|
|
36
|
+
# 'gcp-eu-api.contentstack.com'
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
_regions_data = None # in-memory cache — shared across all instances
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def get_contentstack_endpoint(region='us', service='', omit_https=False):
|
|
43
|
+
"""
|
|
44
|
+
Resolve a Contentstack service endpoint URL for a given region.
|
|
45
|
+
|
|
46
|
+
:param region: Region ID or alias ('us', 'eu', 'azure-na', 'gcp-eu', etc.).
|
|
47
|
+
Defaults to 'us' (AWS North America).
|
|
48
|
+
:param service: Service key ('contentDelivery', 'contentManagement', ...).
|
|
49
|
+
When empty, returns a dict of all endpoints for the region.
|
|
50
|
+
:param omit_https: When True, strips 'https://' prefix from returned URL(s).
|
|
51
|
+
:returns: str when service is provided, dict[str,str] otherwise.
|
|
52
|
+
:raises ValueError: When region is empty, unknown, or service is not found.
|
|
53
|
+
:raises RuntimeError: When regions.json cannot be read or parsed.
|
|
54
|
+
"""
|
|
55
|
+
if not region:
|
|
56
|
+
raise ValueError('Empty region provided. Please put valid region.')
|
|
57
|
+
|
|
58
|
+
data = Endpoint._load_regions()
|
|
59
|
+
normalized = region.strip().lower()
|
|
60
|
+
region_row = Endpoint._find_region(data['regions'], normalized)
|
|
61
|
+
|
|
62
|
+
if region_row is None:
|
|
63
|
+
raise ValueError(f'Invalid region: {region}')
|
|
64
|
+
|
|
65
|
+
if service:
|
|
66
|
+
if service not in region_row['endpoints']:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f'Service "{service}" not found for region "{region_row["id"]}"'
|
|
69
|
+
)
|
|
70
|
+
url = region_row['endpoints'][service]
|
|
71
|
+
return Endpoint._strip_https(url) if omit_https else url
|
|
72
|
+
|
|
73
|
+
endpoints = region_row['endpoints']
|
|
74
|
+
if omit_https:
|
|
75
|
+
return {k: Endpoint._strip_https(v) for k, v in endpoints.items()}
|
|
76
|
+
return dict(endpoints)
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _load_regions():
|
|
80
|
+
"""
|
|
81
|
+
Load and cache regions.json.
|
|
82
|
+
|
|
83
|
+
Resolution order:
|
|
84
|
+
1. In-memory static cache (zero I/O after first call)
|
|
85
|
+
2. contentstack_management/data/regions.json on disk (written by download script)
|
|
86
|
+
3. Live download from artifacts.contentstack.com (fallback)
|
|
87
|
+
"""
|
|
88
|
+
if Endpoint._regions_data is not None:
|
|
89
|
+
return Endpoint._regions_data
|
|
90
|
+
|
|
91
|
+
data_dir = os.path.join(os.path.dirname(__file__), 'data')
|
|
92
|
+
path = os.path.join(data_dir, 'regions.json')
|
|
93
|
+
|
|
94
|
+
if not os.path.exists(path):
|
|
95
|
+
Endpoint._download_and_save(path)
|
|
96
|
+
|
|
97
|
+
if not os.path.exists(path):
|
|
98
|
+
raise RuntimeError(
|
|
99
|
+
'contentstack-management: regions.json not found and could not be downloaded. '
|
|
100
|
+
'Run "python scripts/download_regions.py" and ensure network access.'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
105
|
+
decoded = json.load(f)
|
|
106
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
107
|
+
raise RuntimeError(
|
|
108
|
+
f'contentstack-management: Could not read or parse regions.json: {exc}. '
|
|
109
|
+
'Run "python scripts/download_regions.py" to re-download it.'
|
|
110
|
+
) from exc
|
|
111
|
+
|
|
112
|
+
if not isinstance(decoded, dict) or 'regions' not in decoded:
|
|
113
|
+
raise RuntimeError(
|
|
114
|
+
'contentstack-management: regions.json is corrupt. '
|
|
115
|
+
'Run "python scripts/download_regions.py" to re-download it.'
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
Endpoint._regions_data = decoded
|
|
119
|
+
return Endpoint._regions_data
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def _download_and_save(dest):
|
|
123
|
+
"""
|
|
124
|
+
Download regions.json from the Contentstack CDN and save to disk.
|
|
125
|
+
Uses the requests library (already an SDK dependency).
|
|
126
|
+
Silent on failure — the caller decides whether a missing file is fatal.
|
|
127
|
+
|
|
128
|
+
:param dest: Absolute path to write the file to.
|
|
129
|
+
"""
|
|
130
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
import requests
|
|
134
|
+
response = requests.get(REGIONS_URL, timeout=30)
|
|
135
|
+
response.raise_for_status()
|
|
136
|
+
data = response.text
|
|
137
|
+
except Exception: # noqa: BLE001
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
decoded = json.loads(data)
|
|
142
|
+
except json.JSONDecodeError:
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
if isinstance(decoded, dict) and 'regions' in decoded:
|
|
146
|
+
try:
|
|
147
|
+
with open(dest, 'w', encoding='utf-8') as f:
|
|
148
|
+
f.write(data)
|
|
149
|
+
except OSError:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def _find_region(regions, input_str):
|
|
154
|
+
"""
|
|
155
|
+
Find a region entry by its id or any alias (case-insensitive).
|
|
156
|
+
|
|
157
|
+
Two-pass: exact id match first, then alias[] scan — mirrors PHP implementation.
|
|
158
|
+
|
|
159
|
+
:param regions: list of region dicts from regions.json
|
|
160
|
+
:param input_str: already-lowercased input
|
|
161
|
+
:returns: region dict or None
|
|
162
|
+
"""
|
|
163
|
+
for row in regions:
|
|
164
|
+
if row['id'] == input_str:
|
|
165
|
+
return row
|
|
166
|
+
for row in regions:
|
|
167
|
+
for alias in row.get('alias', []):
|
|
168
|
+
if alias.lower() == input_str:
|
|
169
|
+
return row
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def _strip_https(url):
|
|
174
|
+
"""Strip the https:// (or http://) scheme from a URL string."""
|
|
175
|
+
return re.sub(r'^https?://', '', url)
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def reset_cache():
|
|
179
|
+
"""Reset the internal region cache. Intended for testing only."""
|
|
180
|
+
Endpoint._regions_data = None
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility to pull the latest regions.json from the Contentstack CDN and
|
|
3
|
+
overwrite the bundled copy at contentstack_management/data/regions.json.
|
|
4
|
+
|
|
5
|
+
Exposed as a package-level function so tooling and CI pipelines can call it
|
|
6
|
+
programmatically instead of invoking the script directly:
|
|
7
|
+
|
|
8
|
+
from contentstack_management import refresh_regions
|
|
9
|
+
refresh_regions()
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import urllib.request
|
|
16
|
+
|
|
17
|
+
_REGIONS_URL = "https://artifacts.contentstack.com/regions.json"
|
|
18
|
+
_ASSET_PATH = os.path.join(os.path.dirname(__file__), "data", "regions.json")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def refresh_regions(
|
|
22
|
+
url: str = _REGIONS_URL,
|
|
23
|
+
dest: str = _ASSET_PATH,
|
|
24
|
+
*,
|
|
25
|
+
timeout: int = 30,
|
|
26
|
+
silent: bool = False,
|
|
27
|
+
) -> dict:
|
|
28
|
+
"""
|
|
29
|
+
Download the latest regions manifest from the Contentstack CDN and write
|
|
30
|
+
it to the bundled data file so all consumers get the update.
|
|
31
|
+
|
|
32
|
+
@param url - URL to fetch regions.json from (defaults to Contentstack CDN)
|
|
33
|
+
@param dest - Destination file path (defaults to contentstack_management/data/regions.json)
|
|
34
|
+
@param timeout - HTTP request timeout in seconds
|
|
35
|
+
@param silent - Suppress progress output when True
|
|
36
|
+
@returns The parsed regions dict on success
|
|
37
|
+
@raises RuntimeError on download failure, invalid JSON, or unexpected schema
|
|
38
|
+
"""
|
|
39
|
+
dest = os.path.normpath(dest)
|
|
40
|
+
|
|
41
|
+
if not silent:
|
|
42
|
+
print(f"Fetching {url} ...")
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
with urllib.request.urlopen(url, timeout=timeout) as resp:
|
|
46
|
+
data = resp.read().decode("utf-8")
|
|
47
|
+
except Exception as exc:
|
|
48
|
+
raise RuntimeError(f"Could not download regions.json: {exc}") from exc
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
decoded = json.loads(data)
|
|
52
|
+
except json.JSONDecodeError as exc:
|
|
53
|
+
raise RuntimeError(f"Downloaded content is not valid JSON: {exc}") from exc
|
|
54
|
+
|
|
55
|
+
if not isinstance(decoded, dict) or "regions" not in decoded:
|
|
56
|
+
raise RuntimeError("Downloaded JSON does not contain a 'regions' key.")
|
|
57
|
+
|
|
58
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
59
|
+
with open(dest, "w", encoding="utf-8") as fh:
|
|
60
|
+
json.dump(decoded, fh, indent=2, ensure_ascii=False)
|
|
61
|
+
fh.write("\n")
|
|
62
|
+
|
|
63
|
+
region_count = len(decoded["regions"])
|
|
64
|
+
if not silent:
|
|
65
|
+
print(f"OK: Wrote {region_count} regions to {dest}")
|
|
66
|
+
|
|
67
|
+
return decoded
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _cli_main() -> int:
|
|
71
|
+
"""Entry point kept for backward compatibility with the scripts/ invocation."""
|
|
72
|
+
try:
|
|
73
|
+
refresh_regions()
|
|
74
|
+
return 0
|
|
75
|
+
except RuntimeError as exc:
|
|
76
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
77
|
+
return 1
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
sys.exit(_cli_main())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentstack-management
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.0
|
|
4
4
|
Summary: Contentstack API Client Library for Python
|
|
5
5
|
Home-page: https://github.com/contentstack/contentstack-management-python
|
|
6
6
|
Author: dev-ex
|
|
@@ -12,13 +12,13 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Requires-Python: >=3.9
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: bson>=0.5.9
|
|
16
15
|
Requires-Dist: requests>=2.5.4
|
|
17
16
|
Requires-Dist: requests-toolbelt>=0.3.1
|
|
18
|
-
Requires-Dist: urllib3
|
|
17
|
+
Requires-Dist: urllib3<3.0.0,>=2.7.0
|
|
19
18
|
Provides-Extra: dev
|
|
20
19
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
21
20
|
Requires-Dist: twine>=4.0.2; extra == "dev"
|
|
21
|
+
Requires-Dist: packaging>=24.0; extra == "dev"
|
|
22
22
|
Requires-Dist: dotenv>=0.0.5; extra == "dev"
|
|
23
23
|
Dynamic: author
|
|
24
24
|
Dynamic: author-email
|
|
@@ -8,6 +8,8 @@ contentstack_management/_errors.py
|
|
|
8
8
|
contentstack_management/_messages.py
|
|
9
9
|
contentstack_management/common.py
|
|
10
10
|
contentstack_management/contentstack.py
|
|
11
|
+
contentstack_management/endpoint.py
|
|
12
|
+
contentstack_management/region_refresh.py
|
|
11
13
|
contentstack_management.egg-info/PKG-INFO
|
|
12
14
|
contentstack_management.egg-info/SOURCES.txt
|
|
13
15
|
contentstack_management.egg-info/dependency_links.txt
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
|
+
import sys
|
|
3
4
|
from setuptools import setup, find_packages
|
|
5
|
+
from setuptools.command.build_py import build_py
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BuildPyWithRegions(build_py):
|
|
9
|
+
"""Fetch latest regions.json from Contentstack CDN before packaging."""
|
|
10
|
+
|
|
11
|
+
def run(self):
|
|
12
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
13
|
+
try:
|
|
14
|
+
from contentstack_management.region_refresh import refresh_regions
|
|
15
|
+
refresh_regions()
|
|
16
|
+
except Exception as exc:
|
|
17
|
+
print(f"WARNING: Could not refresh regions.json: {exc}", file=sys.stderr)
|
|
18
|
+
super().run()
|
|
4
19
|
|
|
5
20
|
with open("README.md", "r") as f:
|
|
6
21
|
long_description = f.read()
|
|
@@ -33,9 +48,11 @@ def get_author_email(package):
|
|
|
33
48
|
init_py, re.MULTILINE).group(1)
|
|
34
49
|
|
|
35
50
|
setup(
|
|
51
|
+
cmdclass={"build_py": BuildPyWithRegions},
|
|
36
52
|
name="contentstack-management",
|
|
37
53
|
version=get_version(package),
|
|
38
54
|
packages=find_packages(exclude=['tests']),
|
|
55
|
+
package_data={'contentstack_management': ['data/regions.json']},
|
|
39
56
|
py_modules=['_api_client', 'contentstack','common','_errors','_constant'],
|
|
40
57
|
description="Contentstack API Client Library for Python",
|
|
41
58
|
long_description=long_description,
|
|
@@ -50,13 +67,12 @@ setup(
|
|
|
50
67
|
"Operating System :: OS Independent",
|
|
51
68
|
],
|
|
52
69
|
install_requires=[
|
|
53
|
-
"bson >= 0.5.9",
|
|
54
70
|
"requests >= 2.5.4",
|
|
55
71
|
"requests-toolbelt >= 0.3.1",
|
|
56
|
-
"urllib3 >= 2.
|
|
72
|
+
"urllib3 >= 2.7.0,<3.0.0",
|
|
57
73
|
],
|
|
58
74
|
extras_require={
|
|
59
|
-
"dev": ["pytest>=7.0", "twine>=4.0.2", "dotenv>=0.0.5"],
|
|
75
|
+
"dev": ["pytest>=7.0", "twine>=4.0.2", "packaging>=24.0", "dotenv>=0.0.5"],
|
|
60
76
|
},
|
|
61
77
|
python_requires=">=3.9",
|
|
62
78
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/_errors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentstack_management-1.8.0 → contentstack_management-1.10.0}/contentstack_management/common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|