diff --git a/.talismanrc b/.talismanrc index 83861a19..7d973881 100644 --- a/.talismanrc +++ b/.talismanrc @@ -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 @@ -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" + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 02ef5456..54b8801b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/assets/regions.json b/lib/assets/regions.json new file mode 100644 index 00000000..eafbba92 --- /dev/null +++ b/lib/assets/regions.json @@ -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" + } + } + ] +} diff --git a/lib/contentstack.js b/lib/contentstack.js index 02088318..2a73544c 100644 --- a/lib/contentstack.js +++ b/lib/contentstack.js @@ -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 @@ -170,18 +161,10 @@ 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'] + defaultHostName = getRegionEndpoint(params.region.toLowerCase()) } const defaultParameter = { diff --git a/lib/core/Util.js b/lib/core/Util.js index 586c9d29..21aa43b4 100644 --- a/lib/core/Util.js +++ b/lib/core/Util.js @@ -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) { @@ -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?:\/\//, '') +} diff --git a/lib/core/contentstackHTTPClient.js b/lib/core/contentstackHTTPClient.js index c1e12770..9a3e2a68 100644 --- a/lib/core/contentstackHTTPClient.js +++ b/lib/core/contentstackHTTPClient.js @@ -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 = { @@ -68,25 +68,11 @@ 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') - } - - if (uiHostName) { - uiHostName = uiHostName.replace('api', 'app') - } + const region = config.region || 'na' + const uiHostName = getRegionEndpoint(region, 'application') + const developerHubBaseUrl = getRegionEndpoint(region, 'developerHub').replace(/^/, 'https://') 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 - // set ui host name const axiosOptions = { // Axios diff --git a/package-lock.json b/package-lock.json index 2aea0186..42b686fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,13 @@ { "name": "@contentstack/management", - "version": "1.25.1", + "version": "1.25.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/management", - "version": "1.25.1", + "version": "1.25.2", + "hasInstallScript": true, "license": "MIT", "dependencies": { "assert": "^2.1.0", @@ -135,6 +136,7 @@ "version": "7.28.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -2940,7 +2942,6 @@ "version": "1.19.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -2955,7 +2956,6 @@ "version": "3.4.38", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*" } @@ -2999,7 +2999,6 @@ "version": "5.0.6", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -3027,8 +3026,7 @@ "node_modules/@types/http-errors": { "version": "2.0.4", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", @@ -3093,6 +3091,7 @@ "version": "14.1.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" @@ -3106,8 +3105,7 @@ "node_modules/@types/mime": { "version": "1.3.5", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/minimatch": { "version": "5.1.2", @@ -3140,14 +3138,12 @@ "node_modules/@types/qs": { "version": "6.9.18", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/retry": { "version": "0.12.0", @@ -3158,7 +3154,6 @@ "version": "0.17.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -3168,7 +3163,6 @@ "version": "1.15.7", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*", @@ -3404,6 +3398,7 @@ "version": "8.15.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3446,6 +3441,7 @@ "version": "6.12.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3747,10 +3743,11 @@ } }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.1.tgz", + "integrity": "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -4309,6 +4306,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -5362,6 +5360,7 @@ "version": "8.57.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5491,6 +5490,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -5542,6 +5542,7 @@ "version": "9.2.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "eslint-plugin-es": "^1.4.1", "eslint-utils": "^1.4.2", @@ -5561,6 +5562,7 @@ "version": "4.3.1", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": ">=6" } @@ -5583,6 +5585,7 @@ } ], "license": "MIT", + "peer": true, "peerDependencies": { "eslint": ">=5.0.0" } @@ -6238,6 +6241,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/fsu": { "version": "1.1.1", "dev": true, @@ -7526,6 +7544,7 @@ "version": "28.1.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^28.1.3", "@jest/types": "^28.1.3", @@ -9141,6 +9160,7 @@ "version": "14.1.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -12083,6 +12103,7 @@ "version": "8.17.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12434,6 +12455,7 @@ "version": "4.9.5", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12658,6 +12680,7 @@ "version": "5.101.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -12705,6 +12728,7 @@ "version": "6.0.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", @@ -12775,6 +12799,7 @@ "version": "8.17.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", diff --git a/package.json b/package.json index c80d580a..92c7cda9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/management", - "version": "1.25.1", + "version": "1.25.2", "description": "The Content Management API is used to manage the content of your Contentstack account", "main": "./dist/node/contentstack-management.js", "browser": "./dist/web/contentstack-management.js", @@ -44,7 +44,9 @@ "pre-commit": "npm run lint && husky install && husky && chmod +x .husky/pre-commit && ./.husky/pre-commit", "prepush": "npm run test:unit", "generate:docs": "node_modules/.bin/jsdoc --configure .jsdoc.json --readme README.md --verbose", - "husky-check": "npx husky && chmod +x .husky/pre-commit" + "husky-check": "npx husky && chmod +x .husky/pre-commit", + "postinstall": "curl -s --max-time 30 --fail https://artifacts.contentstack.com/regions.json -o lib/assets/regions.json || echo 'Warning: Failed to download regions.json, using existing file if available'", + "postupdate": "curl -s --max-time 30 --fail https://artifacts.contentstack.com/regions.json -o lib/assets/regions.json || echo 'Warning: Failed to download regions.json, using existing file if available'" }, "engines": { "node": ">=8.0.0" diff --git a/test/unit/ContentstackHTTPClient-test.js b/test/unit/ContentstackHTTPClient-test.js index 1b78a290..685575f3 100644 --- a/test/unit/ContentstackHTTPClient-test.js +++ b/test/unit/ContentstackHTTPClient-test.js @@ -167,4 +167,169 @@ describe('Contentstack HTTP Client', () => { expect(axiosInstance.defaults.headers['x-header-ea']).to.be.equal('ea1,ea2') done() }) + + describe('Region-based endpoint configuration', () => { + it('should configure endpoints for NA region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'api.contentstack.io', + region: 'na' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://app.contentstack.com', 'NA UI base URL should match') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('developerhub-api.contentstack.com', 'NA developer hub should match') + done() + }) + + it('should configure endpoints for EU region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'eu-api.contentstack.com', + region: 'eu' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://eu-app.contentstack.com', 'EU UI base URL should match') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('eu-developerhub-api.contentstack.com', 'EU developer hub should match') + done() + }) + + it('should configure endpoints for AU region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'au-api.contentstack.com', + region: 'au' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://au-app.contentstack.com', 'AU UI base URL should match') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('au-developerhub-api.contentstack.com', 'AU developer hub should match') + done() + }) + + it('should configure endpoints for Azure NA region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'azure-na-api.contentstack.com', + region: 'azure-na' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://azure-na-app.contentstack.com', 'Azure NA UI base URL should match') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('azure-na-developerhub-api.contentstack.com', 'Azure NA developer hub should match') + done() + }) + + it('should configure endpoints for Azure EU region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'azure-eu-api.contentstack.com', + region: 'azure-eu' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://azure-eu-app.contentstack.com', 'Azure EU UI base URL should match') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('azure-eu-developerhub-api.contentstack.com', 'Azure EU developer hub should match') + done() + }) + + it('should configure endpoints for GCP NA region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'gcp-na-api.contentstack.com', + region: 'gcp-na' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://gcp-na-app.contentstack.com', 'GCP NA UI base URL should match') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('gcp-na-developerhub-api.contentstack.com', 'GCP NA developer hub should match') + done() + }) + + it('should configure endpoints for GCP EU region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'gcp-eu-api.contentstack.com', + region: 'gcp-eu' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://gcp-eu-app.contentstack.com', 'GCP EU UI base URL should match') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('gcp-eu-developerhub-api.contentstack.com', 'GCP EU developer hub should match') + done() + }) + + it('should include https protocol in developer hub base URL', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'api.contentstack.io', + region: 'na' + }) + expect(axiosInstance.defaults.developerHubBaseUrl).to.match(/^https:\/\//, 'Developer hub URL should start with https://') + done() + }) + + it('should include https protocol in UI base URL', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'api.contentstack.io', + region: 'na' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.match(/^https:\/\//, 'UI base URL should start with https://') + done() + }) + + it('should configure UI base URL with protocol for NA region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'api.contentstack.io', + region: 'na' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://app.contentstack.com', 'NA UI base URL should include protocol') + done() + }) + + it('should configure UI base URL with protocol for EU region', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'eu-api.contentstack.com', + region: 'eu' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://eu-app.contentstack.com', 'EU UI base URL should include protocol') + done() + }) + + it('should handle region aliases when configuring endpoints', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'api.contentstack.io', + region: 'us' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://app.contentstack.com', 'US alias should map to NA endpoints') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('developerhub-api.contentstack.com', 'US alias should map to NA developer hub') + done() + }) + + it('should handle azure_na alias when configuring endpoints', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'azure-na-api.contentstack.com', + region: 'azure_na' + }) + expect(axiosInstance.defaults.uiBaseUrl).to.be.equal('https://azure-na-app.contentstack.com', 'azure_na alias should work') + expect(axiosInstance.defaults.developerHubBaseUrl).to.contain('azure-na-developerhub-api.contentstack.com', 'azure_na alias should work for developer hub') + done() + }) + + it('should configure region property in config', done => { + var axiosInstance = contentstackHTTPClient({ + apiKey: 'apiKey', + accessToken: 'accessToken', + defaultHostName: 'api.contentstack.io', + region: 'eu' + }) + expect(axiosInstance.defaults.region).to.be.equal('eu', 'Region should be stored in defaults') + done() + }) + }) }) diff --git a/test/unit/Util-test.js b/test/unit/Util-test.js index 7d0d8dcf..99c04134 100644 --- a/test/unit/Util-test.js +++ b/test/unit/Util-test.js @@ -1,4 +1,4 @@ -import getUserAgent, { __RewireAPI__ as getUserAgentRewireApi, isHost } from '../../lib/core/Util.js' +import getUserAgent, { __RewireAPI__ as getUserAgentRewireApi, isHost, getRegionEndpoint } from '../../lib/core/Util.js' import { expect } from 'chai' import { describe, it } from 'mocha' const headerRegEx = /(app|sdk|platform|integration|os) \S+(\/\d+.\d+.\d+(-[\w\d-]+)?)?;/igm @@ -166,4 +166,264 @@ describe('Get User Agent', () => { done() }) }) + + describe('Region Endpoint Retrieval', () => { + describe('Valid regions with ID', () => { + it('should return correct endpoint for NA region', done => { + const endpoint = getRegionEndpoint('na', 'contentManagement') + expect(endpoint).to.be.equal('api.contentstack.io', 'NA region endpoint should match') + done() + }) + + it('should return correct endpoint for EU region', done => { + const endpoint = getRegionEndpoint('eu', 'contentManagement') + expect(endpoint).to.be.equal('eu-api.contentstack.com', 'EU region endpoint should match') + done() + }) + + it('should return correct endpoint for AU region', done => { + const endpoint = getRegionEndpoint('au', 'contentManagement') + expect(endpoint).to.be.equal('au-api.contentstack.com', 'AU region endpoint should match') + done() + }) + + it('should return correct endpoint for Azure NA region', done => { + const endpoint = getRegionEndpoint('azure-na', 'contentManagement') + expect(endpoint).to.be.equal('azure-na-api.contentstack.com', 'Azure NA region endpoint should match') + done() + }) + + it('should return correct endpoint for Azure EU region', done => { + const endpoint = getRegionEndpoint('azure-eu', 'contentManagement') + expect(endpoint).to.be.equal('azure-eu-api.contentstack.com', 'Azure EU region endpoint should match') + done() + }) + + it('should return correct endpoint for GCP NA region', done => { + const endpoint = getRegionEndpoint('gcp-na', 'contentManagement') + expect(endpoint).to.be.equal('gcp-na-api.contentstack.com', 'GCP NA region endpoint should match') + done() + }) + + it('should return correct endpoint for GCP EU region', done => { + const endpoint = getRegionEndpoint('gcp-eu', 'contentManagement') + expect(endpoint).to.be.equal('gcp-eu-api.contentstack.com', 'GCP EU region endpoint should match') + done() + }) + }) + + describe('Valid regions with aliases', () => { + it('should return correct endpoint for US alias', done => { + const endpoint = getRegionEndpoint('us', 'contentManagement') + expect(endpoint).to.be.equal('api.contentstack.io', 'US alias should map to NA region') + done() + }) + + it('should return correct endpoint for aws-na alias', done => { + const endpoint = getRegionEndpoint('aws-na', 'contentManagement') + expect(endpoint).to.be.equal('api.contentstack.io', 'aws-na alias should map to NA region') + done() + }) + + it('should return correct endpoint for aws_na alias', done => { + const endpoint = getRegionEndpoint('aws_na', 'contentManagement') + expect(endpoint).to.be.equal('api.contentstack.io', 'aws_na alias should map to NA region') + done() + }) + + it('should return correct endpoint for aws-eu alias', done => { + const endpoint = getRegionEndpoint('aws-eu', 'contentManagement') + expect(endpoint).to.be.equal('eu-api.contentstack.com', 'aws-eu alias should map to EU region') + done() + }) + + it('should return correct endpoint for azure_na alias', done => { + const endpoint = getRegionEndpoint('azure_na', 'contentManagement') + expect(endpoint).to.be.equal('azure-na-api.contentstack.com', 'azure_na alias should map to Azure NA region') + done() + }) + + it('should return correct endpoint for gcp_na alias', done => { + const endpoint = getRegionEndpoint('gcp_na', 'contentManagement') + expect(endpoint).to.be.equal('gcp-na-api.contentstack.com', 'gcp_na alias should map to GCP NA region') + done() + }) + }) + + describe('Different service endpoints', () => { + it('should return correct application endpoint for NA region', done => { + const endpoint = getRegionEndpoint('na', 'application') + expect(endpoint).to.be.equal('app.contentstack.com', 'NA application endpoint should match') + done() + }) + + it('should return correct developerHub endpoint for NA region', done => { + const endpoint = getRegionEndpoint('na', 'developerHub') + expect(endpoint).to.be.equal('developerhub-api.contentstack.com', 'NA developerHub endpoint should match') + done() + }) + + it('should return correct contentDelivery endpoint for EU region', done => { + const endpoint = getRegionEndpoint('eu', 'contentDelivery') + expect(endpoint).to.be.equal('eu-cdn.contentstack.com', 'EU contentDelivery endpoint should match') + done() + }) + + it('should return correct auth endpoint for Azure NA region', done => { + const endpoint = getRegionEndpoint('azure-na', 'auth') + expect(endpoint).to.be.equal('azure-na-auth-api.contentstack.com', 'Azure NA auth endpoint should match') + done() + }) + + it('should return correct graphqlDelivery endpoint for GCP EU region', done => { + const endpoint = getRegionEndpoint('gcp-eu', 'graphqlDelivery') + expect(endpoint).to.be.equal('gcp-eu-graphql.contentstack.com', 'GCP EU graphqlDelivery endpoint should match') + done() + }) + + it('should return correct preview endpoint for AU region', done => { + const endpoint = getRegionEndpoint('au', 'preview') + expect(endpoint).to.be.equal('au-rest-preview.contentstack.com', 'AU preview endpoint should match') + done() + }) + + it('should return correct images endpoint for NA region', done => { + const endpoint = getRegionEndpoint('na', 'images') + expect(endpoint).to.be.equal('images.contentstack.io', 'NA images endpoint should match') + done() + }) + + it('should return correct automate endpoint for Azure EU region', done => { + const endpoint = getRegionEndpoint('azure-eu', 'automate') + expect(endpoint).to.be.equal('azure-eu-automations-api.contentstack.com', 'Azure EU automate endpoint should match') + done() + }) + + it('should return correct personalize endpoint for GCP NA region', done => { + const endpoint = getRegionEndpoint('gcp-na', 'personalize') + expect(endpoint).to.be.equal('gcp-na-personalize-api.contentstack.com', 'GCP NA personalize endpoint should match') + done() + }) + }) + + describe('Default service parameter', () => { + it('should default to contentManagement service when service parameter is not provided', done => { + const endpoint = getRegionEndpoint('na') + expect(endpoint).to.be.equal('api.contentstack.io', 'Should default to contentManagement service') + done() + }) + + it('should default to contentManagement for EU region', done => { + const endpoint = getRegionEndpoint('eu') + expect(endpoint).to.be.equal('eu-api.contentstack.com', 'Should default to contentManagement service for EU') + done() + }) + }) + + describe('URL protocol stripping', () => { + it('should strip https:// protocol from endpoint', done => { + const endpoint = getRegionEndpoint('na', 'contentManagement') + expect(endpoint).to.not.contain('https://', 'Endpoint should not contain https://') + expect(endpoint).to.not.contain('http://', 'Endpoint should not contain http://') + done() + }) + + it('should strip protocol from application endpoint', done => { + const endpoint = getRegionEndpoint('eu', 'application') + expect(endpoint).to.not.contain('https://', 'Application endpoint should not contain https://') + expect(endpoint).to.not.contain('http://', 'Application endpoint should not contain http://') + done() + }) + }) + + describe('Invalid regions', () => { + it('should throw error for invalid region', done => { + try { + getRegionEndpoint('invalid-region', 'contentManagement') + done(new Error('Should have thrown an error')) + } catch (error) { + expect(error.message).to.contain('Invalid region', 'Error message should indicate invalid region') + expect(error.message).to.contain('invalid-region', 'Error message should contain the invalid region name') + done() + } + }) + + it('should throw error for empty region', done => { + try { + getRegionEndpoint('', 'contentManagement') + done(new Error('Should have thrown an error')) + } catch (error) { + expect(error.message).to.contain('Invalid region', 'Error message should indicate invalid region') + done() + } + }) + + it('should throw error for null region', done => { + try { + getRegionEndpoint(null, 'contentManagement') + done(new Error('Should have thrown an error')) + } catch (error) { + expect(error.message).to.contain('Invalid region', 'Error message should indicate invalid region') + done() + } + }) + + it('should throw error for undefined region', done => { + try { + getRegionEndpoint(undefined, 'contentManagement') + done(new Error('Should have thrown an error')) + } catch (error) { + expect(error.message).to.contain('Invalid region', 'Error message should indicate invalid region') + done() + } + }) + + it('should include available regions in error message', done => { + try { + getRegionEndpoint('invalid', 'contentManagement') + done(new Error('Should have thrown an error')) + } catch (error) { + expect(error.message).to.contain('Allowed regions are:', 'Error should list allowed regions') + expect(error.message).to.contain('na', 'Error should include NA region') + expect(error.message).to.contain('eu', 'Error should include EU region') + expect(error.message).to.contain('au', 'Error should include AU region') + done() + } + }) + }) + + describe('Case sensitivity', () => { + it('should handle lowercase region names', done => { + const endpoint = getRegionEndpoint('na', 'contentManagement') + expect(endpoint).to.be.equal('api.contentstack.io', 'Lowercase region should work') + done() + }) + + it('should be case-sensitive for region names', done => { + try { + getRegionEndpoint('NA', 'contentManagement') + done(new Error('Should have thrown an error for uppercase region')) + } catch (error) { + expect(error.message).to.contain('Invalid region', 'Should throw error for uppercase region') + done() + } + }) + + it('should be case-sensitive for aliases', done => { + try { + getRegionEndpoint('US', 'contentManagement') + done(new Error('Should have thrown an error for uppercase alias')) + } catch (error) { + expect(error.message).to.contain('Invalid region', 'Should throw error for uppercase alias') + done() + } + }) + + it('should accept lowercase Azure region', done => { + const endpoint = getRegionEndpoint('azure-na', 'contentManagement') + expect(endpoint).to.be.equal('azure-na-api.contentstack.com', 'Lowercase Azure region should work') + done() + }) + }) + }) }) diff --git a/test/unit/contentstack-test.js b/test/unit/contentstack-test.js index 067d6460..3cb06916 100644 --- a/test/unit/contentstack-test.js +++ b/test/unit/contentstack-test.js @@ -108,4 +108,173 @@ describe('Contentstack HTTP Client', () => { createClientRewireApi.__ResetDependency__('contentstackClient') done() }) + + describe('Region Configuration', () => { + it('should use default NA region when no region is specified', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client() + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('api.contentstack.io', 'Should default to NA region') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should set EU region endpoint when region is eu', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'eu' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('eu-api.contentstack.com', 'Should use EU region endpoint') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should set AU region endpoint when region is au', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'au' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('au-api.contentstack.com', 'Should use AU region endpoint') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should set Azure NA region endpoint when region is azure-na', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'azure-na' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('azure-na-api.contentstack.com', 'Should use Azure NA region endpoint') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should set Azure EU region endpoint when region is azure-eu', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'azure-eu' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('azure-eu-api.contentstack.com', 'Should use Azure EU region endpoint') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should set GCP NA region endpoint when region is gcp-na', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'gcp-na' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('gcp-na-api.contentstack.com', 'Should use GCP NA region endpoint') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should set GCP EU region endpoint when region is gcp-eu', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'gcp-eu' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('gcp-eu-api.contentstack.com', 'Should use GCP EU region endpoint') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should accept region aliases - us', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'us' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('api.contentstack.io', 'Should accept us alias for NA') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should accept region aliases - aws-na', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'aws-na' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('api.contentstack.io', 'Should accept aws-na alias for NA') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should accept region aliases - aws_na', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'aws_na' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('api.contentstack.io', 'Should accept aws_na alias for NA') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should accept region aliases - azure_na', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'azure_na' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('azure-na-api.contentstack.com', 'Should accept azure_na alias for Azure NA') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should throw error for invalid region', done => { + try { + client({ region: 'invalid-region' }) + done(new Error('Should have thrown an error for invalid region')) + } catch (error) { + expect(error.message).to.contain('Invalid region', 'Error message should indicate invalid region') + expect(error.message).to.contain('invalid-region', 'Error message should include the invalid region name') + done() + } + }) + + it('should handle region parameter case-sensitively', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'na' }) + expect(createHttpClientStub.args[0][0].defaultHostName).to.be.equal('api.contentstack.io', 'Lowercase region should work') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + + it('should pass region to HTTP client configuration', done => { + createClientRewireApi.__Rewire__('client', { create: sinon.stub() }) + const createHttpClientStub = sinon.stub() + createClientRewireApi.__Rewire__('httpClient', createHttpClientStub) + createClientRewireApi.__Rewire__('contentstackClient', sinon.stub().returns({})) + client({ region: 'eu' }) + expect(createHttpClientStub.args[0][0].region).to.be.equal('eu', 'Region should be passed to HTTP client') + createClientRewireApi.__ResetDependency__('httpClient') + createClientRewireApi.__ResetDependency__('contentstackClient') + done() + }) + }) }) diff --git a/types/contentstackClient.d.ts b/types/contentstackClient.d.ts index 3d89f093..0d993fd0 100644 --- a/types/contentstackClient.d.ts +++ b/types/contentstackClient.d.ts @@ -31,6 +31,7 @@ export interface ContentstackToken { export interface ContentstackConfig extends AxiosRequestConfig, ContentstackToken { proxy?: ProxyConfig | false endpoint?: string + region?: string host?: string timeout?: number maxRequests?: number