Skip to content

Commit 80ce21b

Browse files
Merge pull request #144 from contentstack/development
staging PR
2 parents 7c8e873 + b2cf073 commit 80ce21b

File tree

10 files changed

+266
-33
lines changed

10 files changed

+266
-33
lines changed

.talismanrc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ fileignoreconfig:
33
ignore_detectors:
44
- filecontent
55
- filename: package-lock.json
6-
checksum: 52799bf1f9a1c387a74baeecac6c1c08f22bb8abdd2a1f0e689d8ed374b47635
6+
checksum: 61066aedc29ef5bd8904d1ee2384dad828e8f9aab1a6b0360797ec7926e7f8dd
77
- filename: .husky/pre-commit
88
checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
99
- filename: test/request.spec.ts
1010
checksum: 87afd3bb570fd52437404cbe69a39311ad8a8c73bca9d075ecf88652fd3e13f6
1111
- filename: src/lib/request.ts
1212
checksum: 86d761c4f50fcf377e52c98e0c4db6f06be955790fc5a0f2ba8fe32a88c60825
13+
- filename: src/lib/retryPolicy/delivery-sdk-handlers.ts
14+
checksum: 08ccd6342b3adbeb7b85309a034b4df4b2ad905a0cc2a3778ab483b61ba41b9e
15+
- filename: test/retryPolicy/delivery-sdk-handlers.spec.ts
16+
checksum: 6d22d7482aa6dccba5554ae497e5b0c3572357a5cead6f4822ee4428edc12207
1317
version: ""

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## Change log
22

3+
### Version: 1.3.3
4+
#### Date: Oct-27-2025
5+
- Fix: Used common serialize method for query params
6+
37
### Version: 1.3.1
48
#### Date: Sept-01-2025
59
- Fix: Replace URLSearchParams.set() with React Native compatible implementation

package-lock.json

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

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/core",
3-
"version": "1.3.1",
3+
"version": "1.3.3",
44
"type": "commonjs",
55
"main": "./dist/cjs/src/index.js",
66
"types": "./dist/cjs/src/index.d.ts",
@@ -20,12 +20,12 @@
2020
"husky-check": "npx husky install && chmod +x .husky/pre-commit"
2121
},
2222
"dependencies": {
23-
"axios": "^1.11.0",
23+
"axios": "^1.12.2",
2424
"axios-mock-adapter": "^2.1.0",
25+
"husky": "^9.1.7",
2526
"lodash": "^4.17.21",
2627
"qs": "^6.14.0",
27-
"tslib": "^2.8.1",
28-
"husky": "^9.1.7"
28+
"tslib": "^2.8.1"
2929
},
3030
"files": [
3131
"dist/*",

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './lib/contentstack-core';
22
export * from './lib/types';
33
export * from './lib/contentstack-error';
4+
export * from './lib/api-error';
45
export * from './lib/request';
56
export * from './lib/retryPolicy/delivery-sdk-handlers';

src/lib/api-error.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Custom error class for API errors with optimized error handling
3+
*/
4+
export class APIError extends Error {
5+
public error_code: number | string;
6+
public status: number;
7+
public error_message: string;
8+
9+
constructor(message: string, error_code: number | string, status: number) {
10+
super(message);
11+
this.name = 'APIError';
12+
this.error_code = error_code;
13+
this.status = status;
14+
this.error_message = message;
15+
16+
// Remove the stack trace completely to avoid showing internal error handling
17+
this.stack = undefined;
18+
}
19+
20+
/**
21+
* Creates an APIError from an Axios error response
22+
* @param err - The Axios error object
23+
* @returns Formatted APIError with meaningful information
24+
*/
25+
static fromAxiosError(err: any): APIError {
26+
if (err.response?.data) {
27+
return APIError.fromResponseData(err.response.data, err.response.status);
28+
} else if (err.message) {
29+
// For network errors or other non-HTTP errors
30+
return new APIError(err.message, err.code || 'NETWORK_ERROR', 0);
31+
} else {
32+
// Fallback for unknown errors
33+
return new APIError('Unknown error occurred', 'UNKNOWN_ERROR', 0);
34+
}
35+
}
36+
37+
/**
38+
* Creates an APIError from response data
39+
* @param responseData - The response data from the API
40+
* @param status - The HTTP status code
41+
* @returns Formatted APIError
42+
*/
43+
static fromResponseData(responseData: any, status: number): APIError {
44+
// Extract error message with fallback chain
45+
const errorMessage = APIError.extractErrorMessage(responseData);
46+
47+
// Extract error code with fallback chain
48+
const errorCode = APIError.extractErrorCode(responseData, status);
49+
50+
return new APIError(errorMessage, errorCode, status);
51+
}
52+
53+
/**
54+
* Extracts error message from response data with multiple fallback options
55+
*/
56+
private static extractErrorMessage(responseData: any): string {
57+
if (responseData.error_message) return responseData.error_message;
58+
if (responseData.message) return responseData.message;
59+
if (responseData.error) return responseData.error;
60+
if (typeof responseData === 'string') return responseData;
61+
return 'Request failed';
62+
}
63+
64+
/**
65+
* Extracts error code from response data with fallback to status
66+
*/
67+
private static extractErrorCode(responseData: any, status: number): number | string {
68+
if (responseData.error_code) return responseData.error_code;
69+
if (responseData.code) return responseData.code;
70+
return status;
71+
}
72+
}

src/lib/request.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,14 @@
11
import { AxiosInstance } from './types';
2+
import { serialize } from './param-serializer';
3+
import { APIError } from './api-error';
24

35
/**
46
* Handles array parameters properly with & separators
57
* React Native compatible implementation without URLSearchParams.set()
68
*/
79
function serializeParams(params: any): string {
810
if (!params) return '';
9-
10-
const parts: string[] = [];
11-
Object.keys(params).forEach(key => {
12-
const value = params[key];
13-
if (Array.isArray(value)) {
14-
value.forEach(item => {
15-
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`);
16-
});
17-
} else if (value !== null && value !== undefined) {
18-
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
19-
}
20-
});
21-
22-
return parts.join('&');
11+
return serialize(params);
2312
}
2413

2514
/**
@@ -48,6 +37,15 @@ async function makeRequest(instance: AxiosInstance, url: string, requestConfig:
4837
}
4938
}
5039

40+
/**
41+
* Handles and formats errors from Axios requests
42+
* @param err - The error object from Axios
43+
* @returns Formatted error object with meaningful information
44+
*/
45+
function handleRequestError(err: any): Error {
46+
return APIError.fromAxiosError(err);
47+
}
48+
5149
export async function getData(instance: AxiosInstance, url: string, data?: any) {
5250
try {
5351
if (instance.stackConfig && instance.stackConfig.live_preview) {
@@ -87,6 +85,6 @@ export async function getData(instance: AxiosInstance, url: string, data?: any)
8785
throw response;
8886
}
8987
} catch (err: any) {
90-
throw err;
88+
throw handleRequestError(err);
9189
}
9290
}

src/lib/retryPolicy/delivery-sdk-handlers.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,17 @@ export const retryResponseErrorHandler = (error: any, config: any, axiosInstance
8383
}
8484
error.config.retryCount = retryCount;
8585

86-
return axiosInstance(error.config);
86+
// Apply configured delay for retries
87+
return new Promise((resolve, reject) => {
88+
setTimeout(async () => {
89+
try {
90+
const retryResponse = await axiosInstance(error.config);
91+
resolve(retryResponse);
92+
} catch (retryError) {
93+
reject(retryError);
94+
}
95+
}, config.retryDelay || 300); // Use configured delay with fallback
96+
});
8797
}
8898
}
8999

@@ -99,17 +109,22 @@ export const retryResponseErrorHandler = (error: any, config: any, axiosInstance
99109
}
100110
};
101111
const retry = (error: any, config: any, retryCount: number, retryDelay: number, axiosInstance: AxiosInstance) => {
102-
let delayTime: number = retryDelay;
103112
if (retryCount > config.retryLimit) {
104113
return Promise.reject(error);
105114
}
106115

107-
delayTime = config.retryDelay;
116+
// Use the passed retryDelay parameter first, then config.retryDelay, then default
117+
const delayTime = retryDelay || config.retryDelay || 300;
108118
error.config.retryCount = retryCount;
109119

110-
return new Promise(function (resolve) {
111-
return setTimeout(function () {
112-
return resolve(axiosInstance(error.request));
120+
return new Promise(function (resolve, reject) {
121+
return setTimeout(async function () {
122+
try {
123+
const retryResponse = await axiosInstance(error.config);
124+
resolve(retryResponse);
125+
} catch (retryError) {
126+
reject(retryError);
127+
}
113128
}, delayTime);
114129
});
115130
};

test/contentstack-core.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ describe('contentstackCore', () => {
152152
it('should call the onError function when an error occurs', async () => {
153153
const onError = jest.fn();
154154
const options = {
155+
defaultHostname: 'cdn.contentstack.io',
155156
onError,
156157
};
157158

0 commit comments

Comments
 (0)