diff --git a/package-lock.json b/package-lock.json index 6a03c6d..781d02e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2210,8 +2210,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", @@ -2333,7 +2332,6 @@ "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -2750,7 +2748,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2834,7 +2831,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3231,7 +3227,6 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", - "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -3461,7 +3456,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -5109,7 +5103,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5417,7 +5410,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7754,7 +7746,6 @@ "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^28.1.3", "@jest/types": "^28.1.3", @@ -9741,7 +9732,6 @@ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin-prettier.js" }, @@ -11494,7 +11484,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -11741,7 +11730,6 @@ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11982,7 +11970,6 @@ "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", diff --git a/src/index.ts b/src/index.ts index 0a60201..056229a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,3 +4,4 @@ export * from './lib/contentstack-error'; export * from './lib/api-error'; export * from './lib/request'; export * from './lib/retryPolicy/delivery-sdk-handlers'; +export * from './lib/error-messages'; diff --git a/src/lib/api-error.ts b/src/lib/api-error.ts index cc35928..063c0e5 100644 --- a/src/lib/api-error.ts +++ b/src/lib/api-error.ts @@ -1,3 +1,5 @@ +import { ERROR_MESSAGES } from './error-messages'; + /** * Custom error class for API errors with optimized error handling */ @@ -27,10 +29,10 @@ export class APIError extends Error { 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); + return new APIError(err.message, err.code || ERROR_MESSAGES.ERROR_CODES.NETWORK_ERROR, 0); } else { // Fallback for unknown errors - return new APIError('Unknown error occurred', 'UNKNOWN_ERROR', 0); + return new APIError(ERROR_MESSAGES.API.UNKNOWN_ERROR, ERROR_MESSAGES.ERROR_CODES.UNKNOWN_ERROR, 0); } } @@ -58,7 +60,7 @@ export class APIError extends Error { if (responseData.message) return responseData.message; if (responseData.error) return responseData.error; if (typeof responseData === 'string') return responseData; - return 'Request failed'; + return ERROR_MESSAGES.API.REQUEST_FAILED(); } /** diff --git a/src/lib/contentstack-core.ts b/src/lib/contentstack-core.ts index d1f18ef..15a1ac9 100644 --- a/src/lib/contentstack-core.ts +++ b/src/lib/contentstack-core.ts @@ -2,6 +2,7 @@ import { cloneDeep } from 'lodash'; import { serialize } from './param-serializer'; import axios, { AxiosRequestHeaders, getAdapter } from 'axios'; import { AxiosInstance, HttpClientParams } from './types'; +import { ERROR_MESSAGES } from './error-messages'; export function httpClient(options: HttpClientParams): AxiosInstance { const defaultConfig = { @@ -17,12 +18,12 @@ export function httpClient(options: HttpClientParams): AxiosInstance { if (level === 'error') { if (data) { const title = [data.name, data.message].filter((a) => a).join(' - '); - console.error(`[error] ${title}`); + console.error(ERROR_MESSAGES.CONSOLE.ERROR_WITH_TITLE(title)); } return; } if (data !== undefined) { - console.log(`[${level}] ${data}`); + console.log(ERROR_MESSAGES.CONSOLE.LEVEL_WITH_DATA(level, data)); } }, retryCondition: (error: any) => { diff --git a/src/lib/error-messages.ts b/src/lib/error-messages.ts new file mode 100644 index 0000000..10258df --- /dev/null +++ b/src/lib/error-messages.ts @@ -0,0 +1,43 @@ +/** + * Centralized Error Messages + * + * This file contains all error messages used throughout the contentstack-js-core SDK. + * Centralizing error messages makes them easier to maintain, review, and localize. + */ + +export const ERROR_MESSAGES = { + // Console Logging Messages + CONSOLE: { + ERROR_WITH_TITLE: (title: string) => `Error: ${title}. Review the error details and try again.`, + LEVEL_WITH_DATA: (level: string, data: any) => `${level}: ${data}. Review the details and try again.`, + }, + + // API Error Messages + API: { + NETWORK_ERROR: 'Network error occurred. Please check your internet connection and try again.', + UNKNOWN_ERROR: 'An unknown error occurred. Please try again or contact support if the issue persists.', + REQUEST_FAILED: (status?: number) => + status + ? `Request failed with status ${status}. Please review your request and try again.` + : 'Request failed. Please review your request and try again.', + }, + + // Request Handler Messages + REQUEST: { + HOST_REQUIRED_FOR_LIVE_PREVIEW: 'Host is required for live preview. Please provide a valid host parameter in the live_preview configuration.', + }, + + // Retry Policy Messages + RETRY: { + TIMEOUT_EXCEEDED: (timeout: number) => + `Request timeout of ${timeout}ms exceeded. Please try again or increase the timeout value in your configuration.`, + }, + + // Error Codes + ERROR_CODES: { + NETWORK_ERROR: 'NETWORK_ERROR', + UNKNOWN_ERROR: 'UNKNOWN_ERROR', + TIMEOUT: 408, + }, +} as const; + diff --git a/src/lib/request.ts b/src/lib/request.ts index 13e35df..8e77963 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -1,6 +1,7 @@ import { AxiosInstance } from './types'; import { serialize } from './param-serializer'; import { APIError } from './api-error'; +import { ERROR_MESSAGES } from './error-messages'; /** * Handles array parameters properly with & separators @@ -63,7 +64,7 @@ export async function getData(instance: AxiosInstance, url: string, data?: any) // adds protocol so host is replaced and not appended if (livePreviewParams.live_preview && livePreviewParams.live_preview !== 'init') { if (!livePreviewParams.host) { - throw new Error('Host is required for live preview'); + throw new Error(ERROR_MESSAGES.REQUEST.HOST_REQUIRED_FOR_LIVE_PREVIEW); } url = (livePreviewParams.host.startsWith('https://') ? '' : 'https://') + livePreviewParams.host + url; } diff --git a/src/lib/retryPolicy/delivery-sdk-handlers.ts b/src/lib/retryPolicy/delivery-sdk-handlers.ts index 9793cf6..0413646 100644 --- a/src/lib/retryPolicy/delivery-sdk-handlers.ts +++ b/src/lib/retryPolicy/delivery-sdk-handlers.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-throw-literal */ import axios, { InternalAxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'; +import { ERROR_MESSAGES } from '../error-messages'; declare module 'axios' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -35,8 +36,8 @@ export const retryResponseErrorHandler = (error: any, config: any, axiosInstance if (!response) { if (error.code === 'ECONNABORTED') { const customError = { - error_message: `Timeout of ${config.timeout}ms exceeded`, - error_code: 408, + error_message: ERROR_MESSAGES.RETRY.TIMEOUT_EXCEEDED(config.timeout), + error_code: ERROR_MESSAGES.ERROR_CODES.TIMEOUT, errors: null, }; throw customError; // Throw customError object diff --git a/test/api-error.spec.ts b/test/api-error.spec.ts index 5c765a6..fe6ca84 100644 --- a/test/api-error.spec.ts +++ b/test/api-error.spec.ts @@ -74,7 +74,7 @@ describe('APIError', () => { const error = APIError.fromAxiosError(axiosError); expect(error).toBeInstanceOf(APIError); - expect(error.error_message).toBe('Unknown error occurred'); + expect(error.error_message).toBe('An unknown error occurred. Please try again or contact support if the issue persists.'); expect(error.error_code).toBe('UNKNOWN_ERROR'); expect(error.status).toBe(0); }); @@ -169,7 +169,7 @@ describe('APIError', () => { const error = APIError.fromResponseData(responseData, 500); expect(error).toBeInstanceOf(APIError); - expect(error.error_message).toBe('Request failed'); + expect(error.error_message).toBe('Request failed. Please review your request and try again.'); expect(error.error_code).toBe(500); expect(error.status).toBe(500); }); diff --git a/test/contentstack-core.spec.ts b/test/contentstack-core.spec.ts index 1cfd3ab..e69a7d3 100644 --- a/test/contentstack-core.spec.ts +++ b/test/contentstack-core.spec.ts @@ -17,7 +17,7 @@ describe('contentstackCore', () => { const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); httpClient({}).defaults.logHandler('error', error); - expect(consoleErrorSpy).toHaveBeenCalledWith('[error] Error - Something went wrong'); + expect(consoleErrorSpy).toHaveBeenCalledWith('Error: Error - Something went wrong. Review the error details and try again.'); consoleErrorSpy.mockRestore(); }); @@ -36,7 +36,7 @@ describe('contentstackCore', () => { httpClient({}).defaults.logHandler('info', 'Some message'); - expect(consoleLogSpy).toHaveBeenCalledWith('[info] Some message'); + expect(consoleLogSpy).toHaveBeenCalledWith('info: Some message. Review the details and try again.'); consoleLogSpy.mockRestore(); }); diff --git a/test/retryPolicy/delivery-sdk-handlers.spec.ts b/test/retryPolicy/delivery-sdk-handlers.spec.ts index 659cfe8..be464de 100644 --- a/test/retryPolicy/delivery-sdk-handlers.spec.ts +++ b/test/retryPolicy/delivery-sdk-handlers.spec.ts @@ -83,7 +83,7 @@ describe('retryResponseErrorHandler', () => { expect(err).toEqual( expect.objectContaining({ error_code: 408, - error_message: `Timeout of ${config.timeout}ms exceeded`, + error_message: `Request timeout of ${config.timeout}ms exceeded. Please try again or increase the timeout value in your configuration.`, errors: null, }) );