Skip to content

Commit 9c4c07a

Browse files
authored
Merge pull request #2111 from contentstack/fix/DX-3532
fix: Added error details for API failures, including a errors property with detailed information
2 parents 30c5330 + 99763fd commit 9c4c07a

File tree

13 files changed

+92
-125
lines changed

13 files changed

+92
-125
lines changed

package-lock.json

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

packages/contentstack-clone/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"@contentstack/cli-cm-export": "~1.20.1",
1010
"@contentstack/cli-cm-import": "~1.28.0",
1111
"@contentstack/cli-command": "~1.6.1",
12-
"@contentstack/cli-utilities": "~1.14.1",
12+
"@contentstack/cli-utilities": "~1.14.2",
1313
"@oclif/core": "^4.3.0",
1414
"@oclif/plugin-help": "^6.2.28",
1515
"chalk": "^4.1.2",

packages/contentstack-export/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"name": "@contentstack/cli-cm-export",
33
"description": "Contentstack CLI plugin to export content from stack",
4-
"version": "1.20.1",
4+
"version": "1.20.2",
55
"author": "Contentstack",
66
"bugs": "https://github.com/contentstack/cli/issues",
77
"dependencies": {
88
"@contentstack/cli-command": "~1.6.1",
9-
"@contentstack/cli-variants": "~1.3.1",
9+
"@contentstack/cli-variants": "~1.3.3",
1010
"@oclif/core": "^4.3.3",
1111
"@contentstack/cli-utilities": "~1.14.1",
1212
"async": "^3.2.6",

packages/contentstack-import/src/import/modules/base-class.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ import { LabelData } from '@contentstack/management/types/stack/label';
1616
import { WebhookData } from '@contentstack/management/types/stack/webhook';
1717
import { WorkflowData } from '@contentstack/management/types/stack/workflow';
1818
import { RoleData } from '@contentstack/management/types/stack/role';
19-
20-
import { log } from '../../utils';
19+
import { log } from '@contentstack/cli-utilities';
2120
import { ImportConfig, ModuleClassParams } from '../../types';
2221
import cloneDeep from 'lodash/cloneDeep';
2322

@@ -209,7 +208,7 @@ export default abstract class BaseClass {
209208
// info: Batch No. 20 of import assets is complete
210209
if (currentIndexer) batchMsg += `Current chunk processing is (${currentIndexer}/${indexerCount})`;
211210

212-
log(this.importConfig, `Batch No. (${batchNo}/${totelBatches}) of ${processName} is complete`, 'success');
211+
log.debug(`Batch No. (${batchNo}/${totelBatches}) of ${processName} is complete`, this.importConfig?.context);
213212
}
214213

215214
if (this.importConfig.modules.assets.displayExecutionTime) {
@@ -325,20 +324,20 @@ export default abstract class BaseClass {
325324
return this.stack.globalField({ api_version: '3.2' }).create(apiData).then(onSuccess).catch(onReject);
326325
case 'update-gfs':
327326
let globalFieldUid = apiData.uid ?? apiData.global_field?.uid;
328-
return this.stack
329-
.globalField(globalFieldUid, { api_version: '3.2' })
330-
.fetch()
331-
.then(async (gf) => {
332-
const { uid, ...updatePayload } = cloneDeep(apiData);
333-
Object.assign(gf, updatePayload);
334-
try {
335-
const response = await gf.update();
336-
return onSuccess(response);
337-
} catch (error) {
338-
return onReject(error);
339-
}
340-
})
341-
.catch(onReject);
327+
return this.stack
328+
.globalField(globalFieldUid, { api_version: '3.2' })
329+
.fetch()
330+
.then(async (gf) => {
331+
const { uid, ...updatePayload } = cloneDeep(apiData);
332+
Object.assign(gf, updatePayload);
333+
try {
334+
const response = await gf.update();
335+
return onSuccess(response);
336+
} catch (error) {
337+
return onReject(error);
338+
}
339+
})
340+
.catch(onReject);
342341
case 'create-environments':
343342
return this.stack
344343
.environment()

packages/contentstack-import/src/import/modules/entries.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,11 +277,11 @@ export default class EntriesImport extends BaseClass {
277277
);
278278

279279
const onSuccess = ({ response: contentType, apiData: { uid } }: any) => {
280-
log.success(`${uid} content type references removed temporarily`, this.importConfig.context);
281-
log.debug(`Successfully processed content type: ${uid}`, this.importConfig.context);
280+
log.success(`'${uid}' content type references removed temporarily`, this.importConfig.context);
281+
log.debug(`Successfully processed content type: '${uid}'`, this.importConfig.context);
282282
};
283283
const onReject = ({ error, apiData: { uid } }: any) => {
284-
handleAndLogError(error, { ...this.importConfig.context, uid }, `${uid} content type references removal failed`);
284+
handleAndLogError(error, { ...this.importConfig.context, uid }, `'${uid}' content type references removal failed`);
285285
};
286286
return await this.makeConcurrentCall({
287287
processName: 'Update content types (removing mandatory references temporarily)',

packages/contentstack-utilities/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/cli-utilities",
3-
"version": "1.14.1",
3+
"version": "1.14.2",
44
"description": "Utilities for contentstack projects",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",

packages/contentstack-utilities/src/authentication-handler.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { cliux as ux, authHandler, configHandler } from './index';
1+
import { cliux as ux, authHandler, configHandler, formatError } from './index';
22

33
class AuthenticationHandler {
44
private authType: string;
@@ -33,7 +33,8 @@ class AuthenticationHandler {
3333
break;
3434
}
3535
} catch (error) {
36-
ux.print(`Error occurred while fetching auth details: ${error?.message}`, {
36+
const formattedError = formatError(error);
37+
ux.print(`Error occurred while fetching auth details: ${formattedError}`, {
3738
color: 'red',
3839
});
3940
throw error;
@@ -75,7 +76,9 @@ class AuthenticationHandler {
7576
if (refreshed) {
7677
return this.refreshAccessToken(error, maxRetryCount); // Retry after refreshing the token
7778
}
78-
console.log('API(401) case error:-', error.response);
79+
80+
const errorDetails = formatError(error);
81+
ux.print(`Authentication failed: ${errorDetails}`, { color: 'red' });
7982
// For Basic Auth, exit immediately without retrying
8083
return;
8184
}
@@ -84,9 +87,11 @@ class AuthenticationHandler {
8487
case 429:
8588
case 408:
8689
if (maxRetryCount >= 3) {
87-
ux.print(`Max retry count reached, please login to proceed, status code: ${error.response.status}`, {
88-
color: 'yellow',
89-
});
90+
const errorDetails = formatError(error);
91+
const statusText = error?.response?.status === 429 ? 'Rate Limited' : 'Request Timeout';
92+
ux.print(`Max retry attempts exceeded (${maxRetryCount}/3)`, { color: 'red' });
93+
ux.print(`Status: ${error?.response?.status} - ${statusText}`, { color: 'yellow' });
94+
ux.print(`Error: ${errorDetails}`, { color: 'white' });
9095
return;
9196
}
9297
maxRetryCount++; // Increment for the next retry attempt
@@ -106,6 +111,8 @@ class AuthenticationHandler {
106111
ux.print('Session timed out, please login to proceed', {
107112
color: 'yellow',
108113
});
114+
ux.print('\nTo fix this:', { color: 'cyan' });
115+
ux.print('• Run: "csdx auth:login"', { color: 'white' });
109116
resolve(false);
110117
} else if (this.authType === 'OAUTH') {
111118
authHandler.host = hostName;
@@ -117,7 +124,9 @@ class AuthenticationHandler {
117124
resolve(true);
118125
})
119126
.catch((error: any) => {
120-
console.log(error);
127+
const errorDetails = formatError(error);
128+
ux.print('OAuth Token Refresh Failed', { color: 'red' });
129+
ux.print(`Error: ${errorDetails}`, { color: 'white' });
121130
resolve(false);
122131
});
123132
} else {

packages/contentstack-utilities/src/helpers.ts

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,29 @@ export const formatError = function (error: any) {
108108
parsedError = error;
109109
}
110110

111+
// Helper function to append error details
112+
const appendErrorDetails = (message: string, errorObj: any): string => {
113+
if (errorObj.errors && typeof errorObj.errors === 'object' && Object.keys(errorObj.errors).length > 0) {
114+
const entityNames: { [key: string]: string } = {
115+
authorization: 'Authentication',
116+
api_key: 'Stack API key',
117+
uid: 'Content Type',
118+
access_token: 'Delivery Token',
119+
};
120+
121+
const errorList = Object.entries(errorObj.errors)
122+
.map(([field, errors]) => {
123+
const errorArray = Array.isArray(errors) ? errors : [errors];
124+
const fieldName = entityNames[field] || field;
125+
return ` • ${fieldName}: ${errorArray.join(', ')}`;
126+
})
127+
.join('\n');
128+
129+
return `${message}\n\nError Details:\n${errorList}\n`;
130+
}
131+
return message;
132+
};
133+
111134
if (parsedError && typeof parsedError === 'object' && Object.keys(parsedError).length === 0) {
112135
if (
113136
!parsedError.message &&
@@ -121,25 +144,25 @@ export const formatError = function (error: any) {
121144
}
122145

123146
if (parsedError?.response?.data?.errorMessage) {
124-
return parsedError.response.data.errorMessage;
147+
return appendErrorDetails(parsedError.response.data.errorMessage, parsedError?.response?.data || parsedError);
125148
}
126149

127150
if (parsedError?.errorMessage) {
128-
return parsedError.errorMessage;
151+
return appendErrorDetails(parsedError.errorMessage, parsedError);
129152
}
130153

131154
const status = parsedError?.status || parsedError?.response?.status;
132155
const errorCode = parsedError?.errorCode || parsedError?.response?.data?.errorCode;
133156
if (status === 422 && errorCode === 104) {
134-
return 'Invalid email or password. Please check your credentials and try again.';
157+
return appendErrorDetails('Invalid email or password. Please check your credentials and try again.', parsedError);
135158
}
136159

137160
if (status === 401) {
138-
return 'Authentication failed. Please check your credentials.';
161+
return appendErrorDetails('Authentication failed. Please check your credentials.', parsedError);
139162
}
140163

141164
if (status === 403) {
142-
return 'Access denied. Please check your permissions.';
165+
return appendErrorDetails('Access denied. Please check your permissions.', parsedError);
143166
}
144167

145168
// Check for specific SSL error
@@ -174,28 +197,8 @@ export const formatError = function (error: any) {
174197
// message is not in JSON format, no need to parse
175198
}
176199

177-
// Append detailed error information if available
178-
if (parsedError.errors && typeof parsedError.errors === 'object' && Object.keys(parsedError.errors).length > 0) {
179-
const entityNames: { [key: string]: string } = {
180-
authorization: 'Authentication',
181-
api_key: 'Stack API key',
182-
uid: 'Content Type',
183-
// deepcode ignore HardcodedNonCryptoSecret: The hardcoded value 'access_token' is used as a key in an error message mapping object and does not represent a sensitive secret or cryptographic key.
184-
access_token: 'Delivery Token',
185-
};
186-
187-
const errorList = Object.entries(parsedError.errors)
188-
.map(([field, errors]) => {
189-
const errorArray = Array.isArray(errors) ? errors : [errors];
190-
const fieldName = entityNames[field] || field;
191-
return ` • ${fieldName}: ${errorArray.join(', ')}`;
192-
})
193-
.join('\n');
194-
195-
message += `\n\nAPI Errors:\n${errorList}`;
196-
}
197-
198-
return message;
200+
// Always append error details at the end
201+
return appendErrorDetails(message, parsedError);
199202
};
200203

201204
/**

packages/contentstack-utilities/src/logger/cli-error-handler.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,6 @@ export default class CLIErrorHandler {
8585
* Extracts a clear, concise error message from various error types.
8686
*/
8787
private extractClearMessage(error: Error & Record<string, any>): string {
88-
if (error?.response?.data?.errorMessage) {
89-
return error.response.data.errorMessage;
90-
}
91-
92-
if (error?.errorMessage) {
93-
return error.errorMessage;
94-
}
95-
9688
// Use existing formatError function for other cases
9789
try {
9890
const formattedMessage = formatError(error);

packages/contentstack-variants/src/import/variant-entries.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
187187
sanitizePath(entry_uid),
188188
);
189189

190-
log.info(`Processing variant entries from: ${variantEntryBasePath}`, this.config.context);
190+
log.debug(`Processing variant entries from: ${variantEntryBasePath}`, this.config.context);
191191
const fs = new FsUtility({ basePath: variantEntryBasePath, createDirIfNotExist: false });
192192

193193
for (const _ in fs.indexFileContent) {
@@ -278,7 +278,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
278278
};
279279

280280
if (variantId) {
281-
log.info(`Creating variant entry for variant ID: ${variantId}`, this.config.context);
281+
log.debug(`Creating variant entry for variant ID: ${variantId}`, this.config.context);
282282
const promise = this.variantInstance.createVariantEntry(
283283
createVariantReq,
284284
{
@@ -420,7 +420,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
420420
* @param variantEntry - The entry variant to update.
421421
*/
422422
updateFileFields(variantEntry: VariantEntryStruct) {
423-
log.info(`Updating file fields for variant entry: ${variantEntry.uid}`, this.config.context);
423+
log.debug(`Updating file fields for variant entry: ${variantEntry.uid}`, this.config.context);
424424

425425
const setValue = (currentObj: VariantEntryStruct, keys: string[]) => {
426426
if (!currentObj || keys.length === 0) return;

0 commit comments

Comments
 (0)