Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
39 changes: 1 addition & 38 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,50 +9,13 @@ on:
jobs:
build-test:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
- name: Check working directory after checkout
run: pwd
- uses: actions/setup-node@v4
with:
node-version: '22.x'
- name: Check working directory after setup-node
run: pwd
- name: Install dependencies
run: |
pwd
npm ci
- name: Download regions.json
run: |
pwd
ls -la
mkdir -p dist/lib
pwd
npm run download-regions
pwd
ls -la dist/lib/ || echo "dist/lib does not exist"
if [ ! -f dist/lib/regions.json ]; then
echo "Error: regions.json was not downloaded successfully"
exit 1
fi
- name: Check working directory before tests
run: |
pwd
ls -la
- uses: ArtiomTr/jest-coverage-report-action@v2
id: coverage-utils-js
continue-on-error: true
with:
output: comment, report-markdown
test-script: npm test
skip-step: install
- uses: marocchino/sticky-pull-request-comment@v2
continue-on-error: true
if: steps.coverage-utils-js.outputs.report != ''
with:
header: Contentstack Utils JS Coverage
recreate: true
Expand All @@ -64,4 +27,4 @@ jobs:
name: JEST Tests
path: reports/junit/jest-*.xml
reporter: jest-junit
fail-on-error: true
fail-on-error: true
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [1.6.1](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.6.1)
- Fix: Improved error handling in getContentstackEndpoint
- Fix: Enhanced error messages for better debugging
- Refactor: Removed redundant try/catch wrapper and improved code structure
- Test: Updated and improved test coverage for error scenarios

## [1.6.0](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.6.0)
- Feat: Adds Helper functions for Contentstack Endpoints

Expand Down
169 changes: 81 additions & 88 deletions __test__/endpoints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import { getContentstackEndpoint, ContentstackEndpoints } from '../src/endpoints
import * as path from 'path';
import * as fs from 'fs';

// Mock console.warn to avoid noise in tests
const originalConsoleWarn = console.warn;

beforeAll(() => {
console.warn = jest.fn();

// Verify build completed - dist/lib/regions.json must exist
// The pretest hook ensures build runs before tests
const regionsPath = path.join(process.cwd(), 'dist', 'lib', 'regions.json');
Expand All @@ -17,10 +12,6 @@ beforeAll(() => {
}
});

afterAll(() => {
console.warn = originalConsoleWarn;
});

describe('getContentstackEndpoint', () => {
describe('Basic functionality', () => {
it('should return default endpoints for valid region without service', () => {
Expand All @@ -38,16 +29,16 @@ describe('getContentstackEndpoint', () => {
expect(result).toBe('https://cdn.contentstack.io');
});

it('should return EU endpoints for EU region', () => {
const result = getContentstackEndpoint('eu', 'contentDelivery');

expect(result).toBe('https://eu-cdn.contentstack.com');
it('should throw error for invalid service', () => {
expect(() => {
getContentstackEndpoint('us', 'invalidService');
}).toThrow(/Service "invalidService" not found for region/);
});

it('should return undefined for invalid service', () => {
const result = getContentstackEndpoint('us', 'invalidService');

expect(result).toBeUndefined();
it('should throw error with exact error message format for invalid service', () => {
expect(() => {
getContentstackEndpoint('us', 'nonexistentService');
}).toThrow('Service "nonexistentService" not found for region "na"');
});
});

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

expect(result).toBe('https://cdn.contentstack.io');
});
});

describe('Error handling and edge cases', () => {
it('should throw error for empty region', () => {
expect(() => {
getContentstackEndpoint('');
}).toThrow('Unable to set the host. Please put valid host');
it('should strip https from EU endpoint when omitHttps is true', () => {
const result = getContentstackEndpoint('eu', 'contentDelivery', true);

expect(result).toBe('eu-cdn.contentstack.com');
});

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

expect(result).toBe('https://cdn.contentstack.io');
expect(result).toBe('azure-na-cdn.contentstack.com');
});

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

expect(result).toBe('https://cdn.contentstack.io');
expect(result).toBe('gcp-na-cdn.contentstack.com');
});

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

expect(result).toBe('https://cdn.contentstack.io');
expect(result.contentDelivery).toBe('eu-cdn.contentstack.com');
expect(result.contentManagement).toBe('eu-api.contentstack.com');
});
});

it('should fallback to default when region is not found', () => {
const result = getContentstackEndpoint('nonexistent', 'contentDelivery');

expect(result).toBe('https://cdn.contentstack.io');
describe('Error handling and edge cases', () => {
it('should throw error for empty region', () => {
expect(() => {
getContentstackEndpoint('');
}).toThrow('Empty region provided. Please put valid region.');
});

it('should throw error for invalid region', () => {
expect(() => {
getContentstackEndpoint('invalid-region', 'contentDelivery');
}).toThrow('Invalid region: invalid-region');
});

it('should throw error when region is not found', () => {
expect(() => {
getContentstackEndpoint('nonexistent', 'contentDelivery');
}).toThrow('Invalid region: nonexistent');
});
});

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

expect(result).toBeDefined();
expect(typeof result).toBe('object');
expect((result as ContentstackEndpoints).contentDelivery).toBe('https://cdn.contentstack.io');
});

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

expect(result).toBe('https://cdn.contentstack.io');
});

it('should return all endpoints when service is empty string', () => {
const result = getContentstackEndpoint('us', '');

expect(result).toBeDefined();
expect(typeof result).toBe('object');
expect((result as ContentstackEndpoints).contentDelivery).toBe('https://cdn.contentstack.io');
expect((result as ContentstackEndpoints).contentManagement).toBe('https://api.contentstack.io');
});
});

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

it('should return correct personalize endpoint', () => {
const result = getContentstackEndpoint('us', 'personalize');
it('should return correct personalizeManagement endpoint', () => {
const result = getContentstackEndpoint('us', 'personalizeManagement');

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

describe('Different regions', () => {
describe('Additional regions and aliases', () => {
it('should return correct EU endpoints', () => {
const result = getContentstackEndpoint('eu', 'contentDelivery');

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

it('should return correct Azure NA endpoints', () => {
const result = getContentstackEndpoint('azure-na', 'contentDelivery');

expect(result).toBe('https://azure-na-cdn.contentstack.com');
});

it('should return correct GCP NA endpoints', () => {
const result = getContentstackEndpoint('gcp-na', 'contentDelivery');

expect(result).toBe('https://gcp-na-cdn.contentstack.com');
});
});

describe('Additional regions and aliases', () => {
it('should return correct Australia endpoints', () => {
const result = getContentstackEndpoint('au', 'contentDelivery');

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

it('should return correct Azure NA endpoints', () => {
const result = getContentstackEndpoint('azure-na', 'contentDelivery');

expect(result).toBe('https://azure-na-cdn.contentstack.com');
});

it('should return correct Azure EU endpoints', () => {
const result = getContentstackEndpoint('azure-eu', 'contentDelivery');

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

it('should return correct GCP NA endpoints', () => {
const result = getContentstackEndpoint('gcp-na', 'contentDelivery');

expect(result).toBe('https://gcp-na-cdn.contentstack.com');
});

it('should return correct GCP EU endpoints', () => {
const result = getContentstackEndpoint('gcp-eu', 'contentDelivery');

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

describe('Edge cases and error scenarios', () => {
it('should handle null region gracefully', () => {
const result = getContentstackEndpoint(null as any, 'contentDelivery');

expect(result).toBe('https://cdn.contentstack.io');
it('should throw error for null region', () => {
expect(() => {
getContentstackEndpoint(null as any, 'contentDelivery');
}).toThrow();
});

it('should handle undefined region gracefully', () => {
it('should use default region for undefined region', () => {
// undefined uses the default parameter 'us', so it doesn't throw
const result = getContentstackEndpoint(undefined as any, 'contentDelivery');

expect(result).toBe('https://cdn.contentstack.io');
});

it('should handle region with only whitespace', () => {
// Whitespace gets trimmed, then normalized to 'us' if empty
// Since ' '.trim() is empty string, it normalizes to 'us'
const result = getContentstackEndpoint(' ', 'contentDelivery');

expect(result).toBe('https://cdn.contentstack.io');
});

it('should handle region with special characters', () => {
const result = getContentstackEndpoint('region@#$%', 'contentDelivery');

expect(result).toBe('https://cdn.contentstack.io');
it('should throw error for region with special characters', () => {
expect(() => {
getContentstackEndpoint('region@#$%', 'contentDelivery');
}).toThrow('Invalid region: region@#$%');
});

it('should handle very long region name', () => {
it('should throw error for very long region name', () => {
const longRegion = 'a'.repeat(1000);
const result = getContentstackEndpoint(longRegion, 'contentDelivery');

expect(result).toBe('https://cdn.contentstack.io');
expect(() => {
getContentstackEndpoint(longRegion, 'contentDelivery');
}).toThrow(`Invalid region: ${longRegion}`);
});
});

describe('Console warnings', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should warn for invalid region', () => {
getContentstackEndpoint('invalid-region', 'contentDelivery');

expect(console.warn).toHaveBeenCalledWith('Invalid region combination.');
});

it('should warn for failed endpoint fetch', () => {
getContentstackEndpoint('invalid-region', 'contentDelivery');

expect(console.warn).toHaveBeenCalledWith('Failed to fetch endpoints:', expect.any(Error));
});
});
});
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default [
console: 'readonly',
__dirname: 'readonly',
require: 'readonly',
process: 'readonly',
},
},
plugins: {
Expand Down
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/utils",
"version": "1.6.0",
"version": "1.6.1",
"description": "Contentstack utilities for Javascript",
"main": "dist/index.es.js",
"types": "dist/types/src/index.d.ts",
Expand Down
Loading
Loading