|
| 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