Skip to content

Commit 33affc1

Browse files
authored
Merge pull request #227 from contentstack/fix/DX-3705
fix: enhance endpoint error handling
2 parents 557f4a5 + e41c26d commit 33affc1

File tree

7 files changed

+141
-181
lines changed

7 files changed

+141
-181
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,6 @@ jobs:
1515
checks: write
1616
steps:
1717
- uses: actions/checkout@v3
18-
- name: Check working directory after checkout
19-
run: pwd
20-
- uses: actions/setup-node@v4
21-
with:
22-
node-version: '22.x'
23-
- name: Check working directory after setup-node
24-
run: pwd
25-
- name: Install dependencies
26-
run: |
27-
pwd
28-
npm ci
29-
- name: Download regions.json
30-
run: |
31-
pwd
32-
ls -la
33-
mkdir -p dist/lib
34-
pwd
35-
npm run download-regions
36-
pwd
37-
ls -la dist/lib/ || echo "dist/lib does not exist"
38-
if [ ! -f dist/lib/regions.json ]; then
39-
echo "Error: regions.json was not downloaded successfully"
40-
exit 1
41-
fi
42-
- name: Check working directory before tests
43-
run: |
44-
pwd
45-
ls -la
4618
- uses: ArtiomTr/jest-coverage-report-action@v2
4719
id: coverage-utils-js
4820
continue-on-error: true

CHANGELOG.md

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

3+
## [1.6.1](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.6.1)
4+
- Fix: Improved error handling in getContentstackEndpoint
5+
- Fix: Enhanced error messages for better debugging
6+
- Refactor: Removed redundant try/catch wrapper and improved code structure
7+
- Test: Updated and improved test coverage for error scenarios
8+
39
## [1.6.0](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.6.0)
410
- Feat: Adds Helper functions for Contentstack Endpoints
511

__test__/endpoints.test.ts

Lines changed: 81 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ import { getContentstackEndpoint, ContentstackEndpoints } from '../src/endpoints
22
import * as path from 'path';
33
import * as fs from 'fs';
44

5-
// Mock console.warn to avoid noise in tests
6-
const originalConsoleWarn = console.warn;
7-
85
beforeAll(() => {
9-
console.warn = jest.fn();
10-
116
// Verify build completed - dist/lib/regions.json must exist
127
// The pretest hook ensures build runs before tests
138
const regionsPath = path.join(process.cwd(), 'dist', 'lib', 'regions.json');
@@ -17,10 +12,6 @@ beforeAll(() => {
1712
}
1813
});
1914

20-
afterAll(() => {
21-
console.warn = originalConsoleWarn;
22-
});
23-
2415
describe('getContentstackEndpoint', () => {
2516
describe('Basic functionality', () => {
2617
it('should return default endpoints for valid region without service', () => {
@@ -38,16 +29,16 @@ describe('getContentstackEndpoint', () => {
3829
expect(result).toBe('https://cdn.contentstack.io');
3930
});
4031

41-
it('should return EU endpoints for EU region', () => {
42-
const result = getContentstackEndpoint('eu', 'contentDelivery');
43-
44-
expect(result).toBe('https://eu-cdn.contentstack.com');
32+
it('should throw error for invalid service', () => {
33+
expect(() => {
34+
getContentstackEndpoint('us', 'invalidService');
35+
}).toThrow(/Service "invalidService" not found for region/);
4536
});
4637

47-
it('should return undefined for invalid service', () => {
48-
const result = getContentstackEndpoint('us', 'invalidService');
49-
50-
expect(result).toBeUndefined();
38+
it('should throw error with exact error message format for invalid service', () => {
39+
expect(() => {
40+
getContentstackEndpoint('us', 'nonexistentService');
41+
}).toThrow('Service "nonexistentService" not found for region "na"');
5142
});
5243
});
5344

@@ -103,40 +94,50 @@ describe('getContentstackEndpoint', () => {
10394

10495
expect(result).toBe('https://cdn.contentstack.io');
10596
});
106-
});
10797

108-
describe('Error handling and edge cases', () => {
109-
it('should throw error for empty region', () => {
110-
expect(() => {
111-
getContentstackEndpoint('');
112-
}).toThrow('Unable to set the host. Please put valid host');
98+
it('should strip https from EU endpoint when omitHttps is true', () => {
99+
const result = getContentstackEndpoint('eu', 'contentDelivery', true);
100+
101+
expect(result).toBe('eu-cdn.contentstack.com');
113102
});
114103

115-
it('should return default endpoint for invalid region', () => {
116-
const result = getContentstackEndpoint('invalid-region', 'contentDelivery');
104+
it('should strip https from Azure endpoint when omitHttps is true', () => {
105+
const result = getContentstackEndpoint('azure-na', 'contentDelivery', true);
117106

118-
expect(result).toBe('https://cdn.contentstack.io');
107+
expect(result).toBe('azure-na-cdn.contentstack.com');
119108
});
120109

121-
it('should return default endpoint for region with underscores/dashes', () => {
122-
const result = getContentstackEndpoint('invalid_region_format', 'contentDelivery');
110+
it('should strip https from GCP endpoint when omitHttps is true', () => {
111+
const result = getContentstackEndpoint('gcp-na', 'contentDelivery', true);
123112

124-
expect(result).toBe('https://cdn.contentstack.io');
113+
expect(result).toBe('gcp-na-cdn.contentstack.com');
125114
});
126115

127-
it('should handle malformed regions data gracefully', () => {
128-
// Note: This test now verifies that invalid regions fallback to default endpoint
129-
// The malformed data scenario is handled by getRegions() throwing an error
130-
// which causes getContentstackEndpoint to fall back to getDefaultEndpoint
131-
const result = getContentstackEndpoint('us', 'contentDelivery', false);
116+
it('should strip https from all endpoints for EU region when omitHttps is true', () => {
117+
const result = getContentstackEndpoint('eu', '', true) as ContentstackEndpoints;
132118

133-
expect(result).toBe('https://cdn.contentstack.io');
119+
expect(result.contentDelivery).toBe('eu-cdn.contentstack.com');
120+
expect(result.contentManagement).toBe('eu-api.contentstack.com');
134121
});
122+
});
135123

136-
it('should fallback to default when region is not found', () => {
137-
const result = getContentstackEndpoint('nonexistent', 'contentDelivery');
138-
139-
expect(result).toBe('https://cdn.contentstack.io');
124+
describe('Error handling and edge cases', () => {
125+
it('should throw error for empty region', () => {
126+
expect(() => {
127+
getContentstackEndpoint('');
128+
}).toThrow('Empty region provided. Please put valid region.');
129+
});
130+
131+
it('should throw error for invalid region', () => {
132+
expect(() => {
133+
getContentstackEndpoint('invalid-region', 'contentDelivery');
134+
}).toThrow('Invalid region: invalid-region');
135+
});
136+
137+
it('should throw error when region is not found', () => {
138+
expect(() => {
139+
getContentstackEndpoint('nonexistent', 'contentDelivery');
140+
}).toThrow('Invalid region: nonexistent');
140141
});
141142
});
142143

@@ -154,13 +155,23 @@ describe('getContentstackEndpoint', () => {
154155

155156
expect(result).toBeDefined();
156157
expect(typeof result).toBe('object');
158+
expect((result as ContentstackEndpoints).contentDelivery).toBe('https://cdn.contentstack.io');
157159
});
158160

159161
it('should use default omitHttps false when not provided', () => {
160162
const result = getContentstackEndpoint('us', 'contentDelivery');
161163

162164
expect(result).toBe('https://cdn.contentstack.io');
163165
});
166+
167+
it('should return all endpoints when service is empty string', () => {
168+
const result = getContentstackEndpoint('us', '');
169+
170+
expect(result).toBeDefined();
171+
expect(typeof result).toBe('object');
172+
expect((result as ContentstackEndpoints).contentDelivery).toBe('https://cdn.contentstack.io');
173+
expect((result as ContentstackEndpoints).contentManagement).toBe('https://api.contentstack.io');
174+
});
164175
});
165176

166177
describe('Service-specific endpoints', () => {
@@ -230,8 +241,8 @@ describe('getContentstackEndpoint', () => {
230241
expect(result).toBe('https://ai.contentstack.com');
231242
});
232243

233-
it('should return correct personalize endpoint', () => {
234-
const result = getContentstackEndpoint('us', 'personalize');
244+
it('should return correct personalizeManagement endpoint', () => {
245+
const result = getContentstackEndpoint('us', 'personalizeManagement');
235246

236247
expect(result).toBe('https://personalize-api.contentstack.com');
237248
});
@@ -243,27 +254,13 @@ describe('getContentstackEndpoint', () => {
243254
});
244255
});
245256

246-
describe('Different regions', () => {
257+
describe('Additional regions and aliases', () => {
247258
it('should return correct EU endpoints', () => {
248259
const result = getContentstackEndpoint('eu', 'contentDelivery');
249260

250261
expect(result).toBe('https://eu-cdn.contentstack.com');
251262
});
252263

253-
it('should return correct Azure NA endpoints', () => {
254-
const result = getContentstackEndpoint('azure-na', 'contentDelivery');
255-
256-
expect(result).toBe('https://azure-na-cdn.contentstack.com');
257-
});
258-
259-
it('should return correct GCP NA endpoints', () => {
260-
const result = getContentstackEndpoint('gcp-na', 'contentDelivery');
261-
262-
expect(result).toBe('https://gcp-na-cdn.contentstack.com');
263-
});
264-
});
265-
266-
describe('Additional regions and aliases', () => {
267264
it('should return correct Australia endpoints', () => {
268265
const result = getContentstackEndpoint('au', 'contentDelivery');
269266

@@ -276,12 +273,24 @@ describe('getContentstackEndpoint', () => {
276273
expect(result).toBe('https://au-cdn.contentstack.com');
277274
});
278275

276+
it('should return correct Azure NA endpoints', () => {
277+
const result = getContentstackEndpoint('azure-na', 'contentDelivery');
278+
279+
expect(result).toBe('https://azure-na-cdn.contentstack.com');
280+
});
281+
279282
it('should return correct Azure EU endpoints', () => {
280283
const result = getContentstackEndpoint('azure-eu', 'contentDelivery');
281284

282285
expect(result).toBe('https://azure-eu-cdn.contentstack.com');
283286
});
284287

288+
it('should return correct GCP NA endpoints', () => {
289+
const result = getContentstackEndpoint('gcp-na', 'contentDelivery');
290+
291+
expect(result).toBe('https://gcp-na-cdn.contentstack.com');
292+
});
293+
285294
it('should return correct GCP EU endpoints', () => {
286295
const result = getContentstackEndpoint('gcp-eu', 'contentDelivery');
287296

@@ -302,53 +311,37 @@ describe('getContentstackEndpoint', () => {
302311
});
303312

304313
describe('Edge cases and error scenarios', () => {
305-
it('should handle null region gracefully', () => {
306-
const result = getContentstackEndpoint(null as any, 'contentDelivery');
307-
308-
expect(result).toBe('https://cdn.contentstack.io');
314+
it('should throw error for null region', () => {
315+
expect(() => {
316+
getContentstackEndpoint(null as any, 'contentDelivery');
317+
}).toThrow();
309318
});
310319

311-
it('should handle undefined region gracefully', () => {
320+
it('should use default region for undefined region', () => {
321+
// undefined uses the default parameter 'us', so it doesn't throw
312322
const result = getContentstackEndpoint(undefined as any, 'contentDelivery');
313-
314323
expect(result).toBe('https://cdn.contentstack.io');
315324
});
316325

317326
it('should handle region with only whitespace', () => {
327+
// Whitespace gets trimmed, then normalized to 'us' if empty
328+
// Since ' '.trim() is empty string, it normalizes to 'us'
318329
const result = getContentstackEndpoint(' ', 'contentDelivery');
319-
320330
expect(result).toBe('https://cdn.contentstack.io');
321331
});
322332

323-
it('should handle region with special characters', () => {
324-
const result = getContentstackEndpoint('region@#$%', 'contentDelivery');
325-
326-
expect(result).toBe('https://cdn.contentstack.io');
333+
it('should throw error for region with special characters', () => {
334+
expect(() => {
335+
getContentstackEndpoint('region@#$%', 'contentDelivery');
336+
}).toThrow('Invalid region: region@#$%');
327337
});
328338

329-
it('should handle very long region name', () => {
339+
it('should throw error for very long region name', () => {
330340
const longRegion = 'a'.repeat(1000);
331-
const result = getContentstackEndpoint(longRegion, 'contentDelivery');
332-
333-
expect(result).toBe('https://cdn.contentstack.io');
341+
expect(() => {
342+
getContentstackEndpoint(longRegion, 'contentDelivery');
343+
}).toThrow(`Invalid region: ${longRegion}`);
334344
});
335345
});
336346

337-
describe('Console warnings', () => {
338-
beforeEach(() => {
339-
jest.clearAllMocks();
340-
});
341-
342-
it('should warn for invalid region', () => {
343-
getContentstackEndpoint('invalid-region', 'contentDelivery');
344-
345-
expect(console.warn).toHaveBeenCalledWith('Invalid region combination.');
346-
});
347-
348-
it('should warn for failed endpoint fetch', () => {
349-
getContentstackEndpoint('invalid-region', 'contentDelivery');
350-
351-
expect(console.warn).toHaveBeenCalledWith('Failed to fetch endpoints:', expect.any(Error));
352-
});
353-
});
354347
});

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default [
1616
console: 'readonly',
1717
__dirname: 'readonly',
1818
require: 'readonly',
19+
process: 'readonly',
1920
},
2021
},
2122
plugins: {

package-lock.json

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/utils",
3-
"version": "1.6.0",
3+
"version": "1.6.1",
44
"description": "Contentstack utilities for Javascript",
55
"main": "dist/index.es.js",
66
"types": "dist/types/src/index.d.ts",

0 commit comments

Comments
 (0)