Skip to content

Commit 3a84192

Browse files
authored
Merge pull request #163 from contentstack/fix/DX-9040
fix: add refresh_regions utility and auto-refresh regions.json at build time.
2 parents f1b882b + 2f47761 commit 3a84192

4 files changed

Lines changed: 106 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
---
55
## v1.10.0
66

7-
#### Date: 08 June 2026
7+
#### Date: 22 June 2026
88

99
- Dynamic region endpoint resolution via the Contentstack Regions Registry (`regions.json`).
1010
- Added `Endpoint` class with 3-tier resolution: in-memory cache → bundled `data/regions.json` → live CDN download.
1111
- Exposed `contentstack_management.get_contentstack_endpoint(region, service, omit_https)` module-level proxy.
1212
- `Client` now resolves the `contentManagement` endpoint from the registry instead of a hardcoded host pattern.
13-
- Added `scripts/download_regions.py` to refresh the bundled registry file.
13+
- Bundled `contentstack_management/data/regions.json` included in `package_data` — always present after `pip install`.
14+
- `setup.py` auto-refreshes `regions.json` at build time via a custom `BuildPyWithRegions` command; network failures warn but never block the build.
15+
- Runtime fallback: if `regions.json` is absent, the SDK downloads it live on the first `Endpoint` call.
1416
- New regions and services require no SDK code changes — registry update is sufficient.
17+
- Added `refresh_regions()` utility to programmatically download the latest regions manifest from the Contentstack CDN and overwrite the bundled `data/regions.json` (`from contentstack_management import refresh_regions`).
18+
- Added `python3 -m contentstack_management.region_refresh` CLI command for refreshing the registry after `pip install` (source-tree script `scripts/download_regions.py` is for contributors only).
1519

1620
---
1721
## v1.9.0

contentstack_management/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from .variants.variants import Variants
3838
from .oauth.oauth_handler import OAuthHandler
3939
from .oauth.oauth_interceptor import OAuthInterceptor
40+
from .region_refresh import refresh_regions
4041

4142

4243
__all__ = (
@@ -77,7 +78,8 @@
7778
"VariantGroup",
7879
"Variants",
7980
"OAuthHandler",
80-
"OAuthInterceptor"
81+
"OAuthInterceptor",
82+
"refresh_regions",
8183
)
8284

8385
def get_contentstack_endpoint(region='us', service='', omit_https=False):
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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())

setup.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
import os
22
import re
3+
import sys
34
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()
419

520
with open("README.md", "r") as f:
621
long_description = f.read()
@@ -33,6 +48,7 @@ def get_author_email(package):
3348
init_py, re.MULTILINE).group(1)
3449

3550
setup(
51+
cmdclass={"build_py": BuildPyWithRegions},
3652
name="contentstack-management",
3753
version=get_version(package),
3854
packages=find_packages(exclude=['tests']),

0 commit comments

Comments
 (0)