diff --git a/CHANGELOG.md b/CHANGELOG.md index d95ce7a..650487d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Change log +### Version: 1.3.3 +#### Date: Oct-27-2025 + - Fix: Used common serialize method for query params + ### Version: 1.3.1 #### Date: Sept-01-2025 - Fix: Replace URLSearchParams.set() with React Native compatible implementation diff --git a/package-lock.json b/package-lock.json index 7c044a8..4a1c224 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@contentstack/core", - "version": "1.3.2", + "version": "1.3.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/core", - "version": "1.3.2", + "version": "1.3.3", "license": "MIT", "dependencies": { "axios": "^1.12.2", diff --git a/package.json b/package.json index d43a6bb..11b9da4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/core", - "version": "1.3.2", + "version": "1.3.3", "type": "commonjs", "main": "./dist/cjs/src/index.js", "types": "./dist/cjs/src/index.d.ts", diff --git a/src/index.ts b/src/index.ts index 01bd35e..0a60201 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ export * from './lib/contentstack-core'; export * from './lib/types'; export * from './lib/contentstack-error'; +export * from './lib/api-error'; export * from './lib/request'; export * from './lib/retryPolicy/delivery-sdk-handlers'; diff --git a/src/lib/api-error.ts b/src/lib/api-error.ts new file mode 100644 index 0000000..cc35928 --- /dev/null +++ b/src/lib/api-error.ts @@ -0,0 +1,72 @@ +/** + * Custom error class for API errors with optimized error handling + */ +export class APIError extends Error { + public error_code: number | string; + public status: number; + public error_message: string; + + constructor(message: string, error_code: number | string, status: number) { + super(message); + this.name = 'APIError'; + this.error_code = error_code; + this.status = status; + this.error_message = message; + + // Remove the stack trace completely to avoid showing internal error handling + this.stack = undefined; + } + + /** + * Creates an APIError from an Axios error response + * @param err - The Axios error object + * @returns Formatted APIError with meaningful information + */ + static fromAxiosError(err: any): APIError { + if (err.response?.data) { + return APIError.fromResponseData(err.response.data, err.response.status); + } else if (err.message) { + // For network errors or other non-HTTP errors + return new APIError(err.message, err.code || 'NETWORK_ERROR', 0); + } else { + // Fallback for unknown errors + return new APIError('Unknown error occurred', 'UNKNOWN_ERROR', 0); + } + } + + /** + * Creates an APIError from response data + * @param responseData - The response data from the API + * @param status - The HTTP status code + * @returns Formatted APIError + */ + static fromResponseData(responseData: any, status: number): APIError { + // Extract error message with fallback chain + const errorMessage = APIError.extractErrorMessage(responseData); + + // Extract error code with fallback chain + const errorCode = APIError.extractErrorCode(responseData, status); + + return new APIError(errorMessage, errorCode, status); + } + + /** + * Extracts error message from response data with multiple fallback options + */ + private static extractErrorMessage(responseData: any): string { + if (responseData.error_message) return responseData.error_message; + if (responseData.message) return responseData.message; + if (responseData.error) return responseData.error; + if (typeof responseData === 'string') return responseData; + return 'Request failed'; + } + + /** + * Extracts error code from response data with fallback to status + */ + private static extractErrorCode(responseData: any, status: number): number | string { + if (responseData.error_code) return responseData.error_code; + if (responseData.code) return responseData.code; + return status; + } +} diff --git a/src/lib/request.ts b/src/lib/request.ts index 5b9000a..13e35df 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -1,4 +1,6 @@ import { AxiosInstance } from './types'; +import { serialize } from './param-serializer'; +import { APIError } from './api-error'; /** * Handles array parameters properly with & separators @@ -6,20 +8,7 @@ import { AxiosInstance } from './types'; */ function serializeParams(params: any): string { if (!params) return ''; - - const parts: string[] = []; - Object.keys(params).forEach(key => { - const value = params[key]; - if (Array.isArray(value)) { - value.forEach(item => { - parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`); - }); - } else if (value !== null && value !== undefined) { - parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); - } - }); - - return parts.join('&'); + return serialize(params); } /** @@ -48,6 +37,15 @@ async function makeRequest(instance: AxiosInstance, url: string, requestConfig: } } +/** + * Handles and formats errors from Axios requests + * @param err - The error object from Axios + * @returns Formatted error object with meaningful information + */ +function handleRequestError(err: any): Error { + return APIError.fromAxiosError(err); +} + export async function getData(instance: AxiosInstance, url: string, data?: any) { try { if (instance.stackConfig && instance.stackConfig.live_preview) { @@ -87,6 +85,6 @@ export async function getData(instance: AxiosInstance, url: string, data?: any) throw response; } } catch (err: any) { - throw err; + throw handleRequestError(err); } } diff --git a/test/contentstack-core.spec.ts b/test/contentstack-core.spec.ts index ab808f6..3767561 100644 --- a/test/contentstack-core.spec.ts +++ b/test/contentstack-core.spec.ts @@ -152,6 +152,7 @@ describe('contentstackCore', () => { it('should call the onError function when an error occurs', async () => { const onError = jest.fn(); const options = { + defaultHostname: 'cdn.contentstack.io', onError, };