Skip to content

Commit edec155

Browse files
committed
feat: Added Endpoint Integration for CMA Python SDK
1 parent b3eb74a commit edec155

9 files changed

Lines changed: 964 additions & 9 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,5 @@ tests/resources/.DS_Store
137137
tests/.DS_Store
138138
tests/resources/.DS_Store
139139
.DS_Store
140+
*/data/regions.json
140141
.talismanrc

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
# CHANGELOG
22

33
## Content Management SDK For Python
4+
---
5+
## v1.10.0
6+
7+
#### Date: 08 June 2026
8+
9+
- Dynamic region endpoint resolution via the Contentstack Regions Registry (`regions.json`).
10+
- Added `Endpoint` class with 3-tier resolution: in-memory cache → bundled `data/regions.json` → live CDN download.
11+
- Exposed `contentstack_management.get_contentstack_endpoint(region, service, omit_https)` module-level proxy.
12+
- `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.
14+
- New regions and services require no SDK code changes — registry update is sufficient.
15+
416
---
517
## v1.9.0
618

ENDPOINT_INTEGRATION.md

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
# Region Endpoint Integration — Management Python SDK
2+
3+
## Overview
4+
5+
Contentstack services are deployed across multiple cloud providers and geographic regions. The Management Python SDK resolves service endpoints dynamically using the Contentstack Regions Registry rather than relying on hardcoded URLs.
6+
7+
This ensures:
8+
9+
- Consistent endpoint resolution across all SDKs
10+
- Automatic support for newly introduced regions
11+
- Automatic support for newly introduced services
12+
- Single source of truth for endpoint configuration
13+
- Elimination of region-specific host logic inside the SDK
14+
15+
---
16+
17+
## Regions Registry
18+
19+
All endpoint information is maintained in the Contentstack Regions Registry.
20+
21+
### Registry URL
22+
23+
```text
24+
https://artifacts.contentstack.com/regions.json
25+
```
26+
27+
The registry contains:
28+
29+
- Region identifiers
30+
- Region aliases
31+
- Default region information
32+
- Service endpoint mappings
33+
34+
### Example
35+
36+
```json
37+
{
38+
"regions": [
39+
{
40+
"id": "na",
41+
"alias": ["us", "aws-na"],
42+
"isDefault": true,
43+
"endpoints": {
44+
"contentDelivery": "https://cdn.contentstack.io",
45+
"contentManagement": "https://api.contentstack.io"
46+
}
47+
}
48+
]
49+
}
50+
```
51+
52+
---
53+
54+
## Endpoint Resolution Contract
55+
56+
The SDK exposes a public endpoint resolution API via the `Endpoint` class.
57+
58+
```text
59+
Endpoint.get_contentstack_endpoint(
60+
region,
61+
service = '',
62+
omit_https = False
63+
)
64+
```
65+
66+
### Parameters
67+
68+
| Parameter | Description |
69+
|-----------|-------------|
70+
| `region` | Region identifier or alias (e.g. `'us'`, `'eu'`, `'azure-na'`) |
71+
| `service` | Service key (e.g. `'contentManagement'`, `'contentDelivery'`). When empty, all endpoints for the region are returned. |
72+
| `omit_https` | When `True`, strips `https://` from the returned URL — useful when constructing the `Client` endpoint string |
73+
74+
### Returns
75+
76+
- `str` — service URL when a service key is provided
77+
- `dict[str, str]` — complete endpoint map when service is omitted
78+
79+
### Raises
80+
81+
| Exception | When |
82+
|-----------|------|
83+
| `ValueError` | Empty region, unknown region, or unknown service |
84+
| `RuntimeError` | `regions.json` cannot be read or parsed |
85+
86+
---
87+
88+
## Region Resolution Rules
89+
90+
Region matching must:
91+
92+
- Ignore case
93+
- Trim whitespace
94+
- Support aliases
95+
- Support both dash (`-`) and underscore (`_`) variants where defined
96+
97+
### Examples
98+
99+
| Input | Resolved Region |
100+
|-------|----------------|
101+
| `na` | `na` |
102+
| `us` | `na` |
103+
| `aws-na` | `na` |
104+
| `AWS_NA` | `na` |
105+
| `eu` | `eu` |
106+
| `azure-na` | `azure-na` |
107+
| `gcp-eu` | `gcp-eu` |
108+
109+
If no region is found:
110+
111+
```text
112+
ValueError: Invalid region
113+
```
114+
115+
---
116+
117+
## Service Resolution Rules
118+
119+
The SDK will:
120+
121+
1. Locate the resolved region in the registry.
122+
2. Locate the service key within the region endpoints.
123+
3. Return the endpoint URL.
124+
125+
### Example
126+
127+
```text
128+
Region: eu
129+
Service: contentManagement
130+
131+
Result:
132+
https://eu-api.contentstack.com
133+
```
134+
135+
If the service key is not present:
136+
137+
```text
138+
ValueError: Service not found
139+
```
140+
141+
---
142+
143+
## Supported Service Keys
144+
145+
- `contentManagement`
146+
- `contentDelivery`
147+
- `graphqlDelivery`
148+
- `graphqlPreview`
149+
- `preview`
150+
- `auth`
151+
- `application`
152+
- `images`
153+
- `assets`
154+
- `automate`
155+
- `launch`
156+
- `developerHub`
157+
- `brandKit`
158+
- `genAI`
159+
- `personalizeManagement`
160+
- `personalizeEdge`
161+
- `composableStudio`
162+
163+
The SDK does not hardcode this list. The registry remains the source of truth.
164+
165+
---
166+
167+
## Registry Loading Requirements
168+
169+
Resolution order:
170+
171+
1. **In-memory cache**`Endpoint._regions_data` class-level variable. Zero I/O after first call.
172+
2. **Local registry file**`contentstack_management/data/regions.json` on disk, bundled at install time.
173+
3. **Live download fallback**`GET https://artifacts.contentstack.com/regions.json` via `requests`.
174+
175+
```text
176+
get_contentstack_endpoint()
177+
178+
179+
In-memory cache hit?
180+
Yes → return
181+
No ↓
182+
183+
regions.json on disk?
184+
Yes → load, cache, return
185+
No ↓
186+
187+
Download from CDN
188+
Success → write to disk, cache, return
189+
Failure → RuntimeError
190+
```
191+
192+
---
193+
194+
## Registry Management
195+
196+
The `regions.json` file is stored locally at `contentstack_management/data/regions.json` and managed via a download script.
197+
198+
> **Note:** The registry is stored under `data/` rather than `assets/` because `contentstack_management/assets/` is already a Python package used for the Assets Management API.
199+
200+
### Initial Download
201+
202+
Run once after cloning or installing the SDK:
203+
204+
```bash
205+
python3 scripts/download_regions.py
206+
```
207+
208+
This downloads the latest `regions.json` from the Contentstack Regions Registry and stores it in:
209+
210+
```text
211+
contentstack_management/data/regions.json
212+
```
213+
214+
### Refresh Registry
215+
216+
To manually refresh and overwrite the existing file:
217+
218+
```bash
219+
python3 scripts/download_regions.py
220+
```
221+
222+
This command:
223+
224+
1. Downloads the latest `regions.json` from the registry.
225+
2. Replaces the existing local copy.
226+
3. Makes newly added regions and services immediately available without requiring SDK code changes.
227+
228+
### Example Workflow
229+
230+
```text
231+
python3 scripts/download_regions.py
232+
233+
234+
Download latest regions.json
235+
236+
237+
Store in contentstack_management/data/regions.json
238+
239+
240+
Available on next Endpoint call
241+
```
242+
243+
> **Note:** If `regions.json` is absent at runtime, the SDK performs a live download automatically on the first `Endpoint` call. The script is recommended for production environments to avoid the startup latency.
244+
245+
---
246+
247+
## SDK Integration
248+
249+
```text
250+
Resolve Region
251+
252+
Resolve contentManagement Endpoint
253+
254+
Configure Client Endpoint
255+
256+
Execute Management API Requests
257+
```
258+
259+
`Client` automatically resolves its endpoint via `Endpoint` during initialization. The endpoint is configured from the resolved `contentManagement` URL rather than a hardcoded hostname.
260+
261+
### Resolution Priority
262+
263+
| Condition | Endpoint Source |
264+
|-----------|----------------|
265+
| Custom `host` provided | Custom host wins — Endpoint is not consulted |
266+
| No custom `host` | `Endpoint.get_contentstack_endpoint(region, 'contentManagement')` |
267+
| Unknown region (fallback) | Legacy pattern: `{region}-api.contentstack.com` |
268+
269+
---
270+
271+
## Error Handling
272+
273+
| Scenario | Exception | Message |
274+
|----------|-----------|---------|
275+
| Empty region | `ValueError` | `Empty region provided. Please put valid region.` |
276+
| Unknown region | `ValueError` | `Invalid region: <value>` |
277+
| Unknown service | `ValueError` | `Service "<key>" not found for region "<id>"` |
278+
| Registry unavailable | `RuntimeError` | `regions.json not found and could not be downloaded.` |
279+
| Registry corrupt | `RuntimeError` | `regions.json is corrupt. Run scripts/download_regions.py to re-download it.` |
280+
281+
---
282+
283+
## Caching Requirements
284+
285+
Goals:
286+
287+
- Avoid repeated disk reads
288+
- Avoid repeated network requests
289+
- Improve endpoint lookup performance
290+
291+
The Management Python SDK uses a class-level variable `Endpoint._regions_data` as the in-memory cache. It is populated on first use and persists for the lifetime of the process. Call `Endpoint.reset_cache()` to invalidate (intended for testing only).
292+
293+
---
294+
295+
## Future Compatibility
296+
297+
SDK implementations must not:
298+
299+
- Hardcode endpoint URLs
300+
- Hardcode region mappings
301+
- Hardcode service mappings
302+
303+
All endpoint information must originate from the Regions Registry.
304+
305+
---
306+
307+
## Management Python SDK Example
308+
309+
```python
310+
import contentstack_management
311+
from contentstack_management.endpoint import Endpoint
312+
from contentstack_management.contentstack import Region, Client
313+
314+
# --- Resolve a single service URL ---
315+
url = Endpoint.get_contentstack_endpoint('eu', 'contentManagement')
316+
# https://eu-api.contentstack.com
317+
318+
# --- Resolve without scheme (for use as a host string) ---
319+
host = Endpoint.get_contentstack_endpoint('gcp-na', 'contentManagement', omit_https=True)
320+
# gcp-na-api.contentstack.com
321+
322+
# --- Resolve all services for a region ---
323+
endpoints = Endpoint.get_contentstack_endpoint('azure-na')
324+
# {
325+
# 'contentDelivery': 'https://azure-na-cdn.contentstack.com',
326+
# 'contentManagement': 'https://azure-na-api.contentstack.com',
327+
# ...
328+
# }
329+
330+
# --- Module-level proxy ---
331+
url = contentstack_management.get_contentstack_endpoint('eu', 'contentManagement')
332+
# https://eu-api.contentstack.com
333+
334+
# --- Client endpoint is auto-resolved via Endpoint ---
335+
client = Client(
336+
authtoken='<AUTHTOKEN>',
337+
region=Region.EU.value
338+
)
339+
# client.endpoint → 'https://eu-api.contentstack.com/v3/'
340+
341+
# Custom host still overrides Endpoint resolution
342+
client = Client(
343+
authtoken='<AUTHTOKEN>',
344+
region='au',
345+
host='custom.example.com'
346+
)
347+
# client.endpoint → 'https://au-api.custom.example.com/v3/'
348+
349+
# --- Make Management API calls using the resolved endpoint ---
350+
response = client.stack('<API_KEY>').content_types().find()
351+
```
352+
353+
---
354+
355+
## Supported Regions
356+
357+
| Region ID | Aliases | Content Management URL |
358+
|-----------|---------|----------------------|
359+
| `na` | `us`, `aws-na`, `aws_na` | `https://api.contentstack.io` |
360+
| `eu` || `https://eu-api.contentstack.com` |
361+
| `au` || `https://au-api.contentstack.com` |
362+
| `azure-na` | `azure_na` | `https://azure-na-api.contentstack.com` |
363+
| `azure-eu` | `azure_eu` | `https://azure-eu-api.contentstack.com` |
364+
| `gcp-na` | `gcp_na` | `https://gcp-na-api.contentstack.com` |
365+
| `gcp-eu` | `gcp_eu` | `https://gcp-eu-api.contentstack.com` |
366+
367+
Region aliases are case-insensitive. `AWS-NA`, `aws-na`, and `aws_na` all resolve to the `na` region.

0 commit comments

Comments
 (0)