Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fileignoreconfig:
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: bab53d56ce2609e960fdbbd1e87cc89915820e6761016ddd74ee57f931f4223d
checksum: 1475ee2c6a615f4e6f8393f4a209398aa6b827e7d036302c6fc065d5914e8292
- filename: .husky/pre-commit
checksum: 52a664f536cf5d1be0bea19cb6031ca6e8107b45b6314fe7d47b7fad7d800632
- filename: test/sanity-check/api/user-test.js
Expand All @@ -30,4 +30,11 @@ fileignoreconfig:
checksum: b76ca091caa3a1b2658cd422a2d8ef3ac9996aea0aff3f982d56bb309a3d9fde
- filename: test/unit/ContentstackClient-test.js
checksum: 974a4f335aef025b657d139bb290233a69bed1976b947c3c674e97baffe4ce2f
- filename: test/unit/ContentstackHTTPClient-test.js
checksum: 4043efd843e24da9afd0272c55ef4b0432e3374b2ca12b913f1a6654df3f62be
- filename: test/unit/contentstack-test.js
checksum: 2597efae3c1ab8cc173d5bf205f1c76932211f8e0eb2a16444e055d83481976c
version: "1.0"



10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [v1.25.2](https://github.com/contentstack/contentstack-management-javascript/tree/v1.25.2) (2025-10-28)
- Fix
- Fixed HTTP client region endpoint initialization to default to 'na' region when region parameter is not provided
- Test
- Added comprehensive test coverage for region endpoint functionality
- Added 48 test cases for getRegionEndpoint function covering all supported regions, aliases, and service endpoints
- Added 14 test cases for region configuration in client initialization
- Added 13 test cases for HTTP client region integration
- All 626 tests passing with no regressions

## [v1.25.1](https://github.com/contentstack/contentstack-management-javascript/tree/v1.25.1) (2025-10-06)
- Fix
- Updated delay handling to use centralized external configuration in SDK interceptor
Expand Down
211 changes: 211 additions & 0 deletions lib/assets/regions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
{
"regions": [
{
"id": "na",
"name": "AWS North America",
"cloudProvider": "AWS",
"location": "North America",
"alias": [
"na",
"us",
"aws-na",
"aws_na"
],
"isDefault": true,
"endpoints": {
"application": "https://app.contentstack.com",
"contentDelivery": "https://cdn.contentstack.io",
"contentManagement": "https://api.contentstack.io",
"auth": "https://auth-api.contentstack.com",
"graphqlDelivery": "https://graphql.contentstack.com",
"preview": "https://rest-preview.contentstack.com",
"graphqlPreview": "https://graphql-preview.contentstack.com",
"images": "https://images.contentstack.io",
"assets": "https://assets.contentstack.io",
"automate": "https://automations-api.contentstack.com",
"launch": "https://launch-api.contentstack.com",
"developerHub": "https://developerhub-api.contentstack.com",
"brandKit": "https://brand-kits-api.contentstack.com",
"genAI": "https://ai.contentstack.com",
"personalize": "https://personalize-api.contentstack.com",
"personalizeEdge": "https://personalize-edge.contentstack.com"
}
},
{
"id": "eu",
"name": "AWS Europe",
"cloudProvider": "AWS",
"location": "Europe",
"alias": [
"eu",
"aws-eu",
"aws_eu"
],
"isDefault": false,
"endpoints": {
"application": "https://eu-app.contentstack.com",
"contentDelivery": "https://eu-cdn.contentstack.com",
"contentManagement": "https://eu-api.contentstack.com",
"auth": "https://eu-auth-api.contentstack.com",
"graphqlDelivery": "https://eu-graphql.contentstack.com",
"preview": "https://eu-rest-preview.contentstack.com",
"graphqlPreview": "https://eu-graphql-preview.contentstack.com",
"images": "https://eu-images.contentstack.com",
"assets": "https://eu-assets.contentstack.com",
"automate": "https://eu-prod-automations-api.contentstack.com",
"launch": "https://eu-launch-api.contentstack.com",
"developerHub": "https://eu-developerhub-api.contentstack.com",
"brandKit": "https://eu-brand-kits-api.contentstack.com",
"genAI": "https://eu-ai.contentstack.com",
"personalize": "https://eu-personalize-api.contentstack.com",
"personalizeEdge": "https://eu-personalize-edge.contentstack.com"
}
},
{
"id": "au",
"name": "AWS Australia",
"cloudProvider": "AWS",
"location": "Australia",
"alias": [
"au",
"aws-au",
"aws_au"
],
"isDefault": false,
"endpoints": {
"application": "https://au-app.contentstack.com",
"contentDelivery": "https://au-cdn.contentstack.com",
"contentManagement": "https://au-api.contentstack.com",
"auth": "https://au-auth-api.contentstack.com",
"graphqlDelivery": "https://au-graphql.contentstack.com",
"preview": "https://au-rest-preview.contentstack.com",
"graphqlPreview": "https://au-graphql-preview.contentstack.com",
"images": "https://au-images.contentstack.com",
"assets": "https://au-assets.contentstack.com",
"automate": "https://au-prod-automations-api.contentstack.com",
"launch": "https://au-launch-api.contentstack.com",
"developerHub": "https://au-developerhub-api.contentstack.com",
"brandKit": "https://au-brand-kits-api.contentstack.com",
"genAI": "https://au-ai.contentstack.com",
"personalize": "https://au-personalize-api.contentstack.com",
"personalizeEdge": "https://au-personalize-edge.contentstack.com"
}
},
{
"id": "azure-na",
"name": "Azure North America",
"cloudProvider": "Azure",
"location": "North America",
"alias": [
"azure-na",
"azure_na"
],
"isDefault": false,
"endpoints": {
"application": "https://azure-na-app.contentstack.com",
"contentDelivery": "https://azure-na-cdn.contentstack.com",
"contentManagement": "https://azure-na-api.contentstack.com",
"auth": "https://azure-na-auth-api.contentstack.com",
"graphqlDelivery": "https://azure-na-graphql.contentstack.com",
"preview": "https://azure-na-rest-preview.contentstack.com",
"graphqlPreview": "https://azure-na-graphql-preview.contentstack.com",
"images": "https://azure-na-images.contentstack.com",
"assets": "https://azure-na-assets.contentstack.com",
"automate": "https://azure-na-automations-api.contentstack.com",
"launch": "https://azure-na-launch-api.contentstack.com",
"developerHub": "https://azure-na-developerhub-api.contentstack.com",
"brandKit": "https://azure-na-brand-kits-api.contentstack.com",
"genAI": "https://azure-na-ai.contentstack.com",
"personalize": "https://azure-na-personalize-api.contentstack.com",
"personalizeEdge": "https://azure-na-personalize-edge.contentstack.com"
}
},
{
"id": "azure-eu",
"name": "Azure Europe",
"cloudProvider": "Azure",
"location": "Europe",
"alias": [
"azure-eu",
"azure_eu"
],
"isDefault": false,
"endpoints": {
"application": "https://azure-eu-app.contentstack.com",
"contentDelivery": "https://azure-eu-cdn.contentstack.com",
"contentManagement": "https://azure-eu-api.contentstack.com",
"auth": "https://azure-eu-auth-api.contentstack.com",
"graphqlDelivery": "https://azure-eu-graphql.contentstack.com",
"preview": "https://azure-eu-rest-preview.contentstack.com",
"graphqlPreview": "https://azure-eu-graphql-preview.contentstack.com",
"images": "https://azure-eu-images.contentstack.com",
"assets": "https://azure-eu-assets.contentstack.com",
"automate": "https://azure-eu-automations-api.contentstack.com",
"launch": "https://azure-eu-launch-api.contentstack.com",
"developerHub": "https://azure-eu-developerhub-api.contentstack.com",
"brandKit": "https://azure-eu-brand-kits-api.contentstack.com",
"genAI": "https://azure-eu-ai.contentstack.com",
"personalize": "https://azure-eu-personalize-api.contentstack.com",
"personalizeEdge": "https://azure-eu-personalize-edge.contentstack.com"
}
},
{
"id": "gcp-na",
"name": "GCP North America",
"cloudProvider": "GCP",
"location": "North America",
"alias": [
"gcp-na",
"gcp_na"
],
"isDefault": false,
"endpoints": {
"application": "https://gcp-na-app.contentstack.com",
"contentDelivery": "https://gcp-na-cdn.contentstack.com",
"contentManagement": "https://gcp-na-api.contentstack.com",
"auth": "https://gcp-na-auth-api.contentstack.com",
"graphqlDelivery": "https://gcp-na-graphql.contentstack.com",
"preview": "https://gcp-na-rest-preview.contentstack.com",
"graphqlPreview": "https://gcp-na-graphql-preview.contentstack.com",
"images": "https://gcp-na-images.contentstack.com",
"assets": "https://gcp-na-assets.contentstack.com",
"automate": "https://gcp-na-automations-api.contentstack.com",
"launch": "https://gcp-na-launch-api.contentstack.com",
"developerHub": "https://gcp-na-developerhub-api.contentstack.com",
"brandKit": "https://gcp-na-brand-kits-api.contentstack.com",
"genAI": "https://gcp-na-brand-kits-api.contentstack.com",
"personalize": "https://gcp-na-personalize-api.contentstack.com",
"personalizeEdge": "https://gcp-na-personalize-edge.contentstack.com"
}
},
{
"id": "gcp-eu",
"name": "GCP Europe",
"cloudProvider": "GCP",
"location": "Europe",
"alias": [
"gcp-eu",
"gcp_eu"
],
"isDefault": false,
"endpoints": {
"application": "https://gcp-eu-app.contentstack.com",
"contentDelivery": "https://gcp-eu-cdn.contentstack.com",
"contentManagement": "https://gcp-eu-api.contentstack.com",
"auth": "https://gcp-eu-auth-api.contentstack.com",
"graphqlDelivery": "https://gcp-eu-graphql.contentstack.com",
"preview": "https://gcp-eu-rest-preview.contentstack.com",
"graphqlPreview": "https://gcp-eu-graphql-preview.contentstack.com",
"images": "https://gcp-eu-images.contentstack.com",
"assets": "https://gcp-eu-assets.contentstack.com",
"automate": "https://gcp-eu-automations-api.contentstack.com",
"launch": "https://gcp-eu-launch-api.contentstack.com",
"developerHub": "https://gcp-eu-developerhub-api.contentstack.com",
"brandKit": "https://gcp-eu-brand-kits-api.contentstack.com",
"genAI": "https://gcp-eu-brand-kits-api.contentstack.com",
"personalize": "https://gcp-eu-personalize-api.contentstack.com",
"personalizeEdge": "https://gcp-eu-personalize-edge.contentstack.com"
}
}
]
}
24 changes: 4 additions & 20 deletions lib/contentstack.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,9 @@
*/
import packages from '../package.json'
import clonedeep from 'lodash/cloneDeep'
import getUserAgent from './core/Util.js'
import getUserAgent, { getRegionEndpoint } from './core/Util.js'
import contentstackClient from './contentstackClient.js'
import httpClient from './core/contentstackHTTPClient.js'
const regionHostMap = {
NA: 'api.contentstack.io',
EU: 'eu-api.contentstack.com',
AU: 'au-api.contentstack.com',
AZURE_NA: 'azure-na-api.contentstack.com',
AZURE_EU: 'azure-eu-api.contentstack.com',
GCP_NA: 'gcp-na-api.contentstack.com',
GCP_EU: 'gcp-eu-api.contentstack.com'
}

/**
* Create client instance
Expand Down Expand Up @@ -170,18 +161,11 @@ const regionHostMap = {
* @returns Contentstack.Client
*/
export function client (params = {}) {
let defaultHostName
let defaultHostName = getRegionEndpoint('na')

if (params.region) {
const region = params.region.toUpperCase()
if (!regionHostMap[region]) {
throw new Error(`Invalid region '${params.region}' provided. Allowed regions are: ${Object.keys(regionHostMap).join(', ')}`)
}
defaultHostName = regionHostMap[region]
} else if (params.host) {
defaultHostName = params.host
} else {
defaultHostName = regionHostMap['NA']
params.region = params.region.toLowerCase()
defaultHostName = getRegionEndpoint(params.region)
}

const defaultParameter = {
Expand Down
13 changes: 13 additions & 0 deletions lib/core/Util.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { platform, release } from 'os'
import regionHostMap from '../assets/regions.json'

const HOST_REGEX = /^(?!(?:(?:https?|ftp):\/\/|internal|localhost|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))(?:[\w-]+\.contentstack\.(?:io|com)(?::[^\/\s:]+)?|[\w-]+(?:\.[\w-]+)*(?::[^\/\s:]+)?)(?![\/?#])$/ // eslint-disable-line

export function isHost (host) {
Expand Down Expand Up @@ -235,3 +237,14 @@ export const validateAndSanitizeConfig = (config) => {
url: config.url.trim() // Sanitize URL by removing whitespace
}
}

export const getRegionEndpoint = (region, service = 'contentManagement') => {
const regionData = regionHostMap.regions.find(r =>
r.id === region ||
r.alias.some(alias => alias === region)
)
if (!regionData) {
throw new Error(`Invalid region '${region}' provided. Allowed regions are: ${regionHostMap.regions.map(r => r.id).join(', ')}`)
}
return regionData.endpoints[service]?.replace(/^https?:\/\//, '')
}
32 changes: 18 additions & 14 deletions lib/core/contentstackHTTPClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios'
import clonedeep from 'lodash/cloneDeep'
import Qs from 'qs'
import { ConcurrencyQueue } from './concurrency-queue'
import { isHost } from './Util'
import { getRegionEndpoint, isHost } from './Util'

export default function contentstackHttpClient (options) {
const defaultConfig = {
Expand Down Expand Up @@ -68,24 +68,28 @@ export default function contentstackHttpClient (options) {
config.basePath = `/${config.basePath.split('/').filter(Boolean).join('/')}`
}
const baseURL = config.endpoint || `${protocol}://${hostname}:${port}${config.basePath}/{api-version}`
let uiHostName = hostname
let developerHubBaseUrl = hostname

if (uiHostName?.endsWith('io')) {
uiHostName = uiHostName.replace('io', 'com')
let region = config.region || 'na'
if (!config.region && config.host) {
const hostRegionMatch = config.host.match(/^([a-z]+-?[a-z]*)-api\./)
if (hostRegionMatch) {
region = hostRegionMatch[1]
}
}

if (uiHostName) {
uiHostName = uiHostName.replace('api', 'app')
let uiHostName, developerHubBaseUrl
if (config.host && (config.host.startsWith('dev') || config.host.startsWith('stag'))) {
uiHostName = config.host.replace('-api.', '-app.')
const transformedHost = config.host
.replace(/^dev\d+/, 'dev')
.replace(/^stag\d+/, 'stag')
developerHubBaseUrl = `https://${transformedHost.replace('-api.', '-developerhub-api.')}`
} else {
uiHostName = getRegionEndpoint(region, 'application')
developerHubBaseUrl = `https://${getRegionEndpoint(region, 'developerHub')}`
}
const uiBaseUrl = config.endpoint || `${protocol}://${uiHostName}`

developerHubBaseUrl = developerHubBaseUrl
?.replace('api', 'developerhub-api')
.replace(/^dev\d+/, 'dev') // Replaces any 'dev1', 'dev2', etc. with 'dev'
.replace('io', 'com')
.replace(/^http/, '') // Removing `http` if already present
.replace(/^/, 'https://') // Adds 'https://' at the start if not already there
const uiBaseUrl = config.endpoint || `${protocol}://${uiHostName}`

// set ui host name
const axiosOptions = {
Expand Down
Loading
Loading