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
10 changes: 5 additions & 5 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 packages/contentstack-clone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"@contentstack/cli-cm-export": "~1.20.1",
"@contentstack/cli-cm-import": "~1.28.0",
"@contentstack/cli-command": "~1.6.1",
"@contentstack/cli-utilities": "~1.14.1",
"@contentstack/cli-utilities": "~1.14.2",
"@oclif/core": "^4.3.0",
"@oclif/plugin-help": "^6.2.28",
"chalk": "^4.1.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/contentstack-export/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@contentstack/cli-cm-export",
"description": "Contentstack CLI plugin to export content from stack",
"version": "1.20.1",
"version": "1.20.2",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
"@contentstack/cli-command": "~1.6.1",
"@contentstack/cli-variants": "~1.3.1",
"@contentstack/cli-variants": "~1.3.3",
"@oclif/core": "^4.3.3",
"@contentstack/cli-utilities": "~1.14.1",
"async": "^3.2.6",
Expand Down
33 changes: 16 additions & 17 deletions packages/contentstack-import/src/import/modules/base-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import { LabelData } from '@contentstack/management/types/stack/label';
import { WebhookData } from '@contentstack/management/types/stack/webhook';
import { WorkflowData } from '@contentstack/management/types/stack/workflow';
import { RoleData } from '@contentstack/management/types/stack/role';

import { log } from '../../utils';
import { log } from '@contentstack/cli-utilities';
import { ImportConfig, ModuleClassParams } from '../../types';
import cloneDeep from 'lodash/cloneDeep';

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

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

if (this.importConfig.modules.assets.displayExecutionTime) {
Expand Down Expand Up @@ -325,20 +324,20 @@ export default abstract class BaseClass {
return this.stack.globalField({ api_version: '3.2' }).create(apiData).then(onSuccess).catch(onReject);
case 'update-gfs':
let globalFieldUid = apiData.uid ?? apiData.global_field?.uid;
return this.stack
.globalField(globalFieldUid, { api_version: '3.2' })
.fetch()
.then(async (gf) => {
const { uid, ...updatePayload } = cloneDeep(apiData);
Object.assign(gf, updatePayload);
try {
const response = await gf.update();
return onSuccess(response);
} catch (error) {
return onReject(error);
}
})
.catch(onReject);
return this.stack
.globalField(globalFieldUid, { api_version: '3.2' })
.fetch()
.then(async (gf) => {
const { uid, ...updatePayload } = cloneDeep(apiData);
Object.assign(gf, updatePayload);
try {
const response = await gf.update();
return onSuccess(response);
} catch (error) {
return onReject(error);
}
})
.catch(onReject);
case 'create-environments':
return this.stack
.environment()
Expand Down
6 changes: 3 additions & 3 deletions packages/contentstack-import/src/import/modules/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,11 @@ export default class EntriesImport extends BaseClass {
);

const onSuccess = ({ response: contentType, apiData: { uid } }: any) => {
log.success(`${uid} content type references removed temporarily`, this.importConfig.context);
log.debug(`Successfully processed content type: ${uid}`, this.importConfig.context);
log.success(`'${uid}' content type references removed temporarily`, this.importConfig.context);
log.debug(`Successfully processed content type: '${uid}'`, this.importConfig.context);
};
const onReject = ({ error, apiData: { uid } }: any) => {
handleAndLogError(error, { ...this.importConfig.context, uid }, `${uid} content type references removal failed`);
handleAndLogError(error, { ...this.importConfig.context, uid }, `'${uid}' content type references removal failed`);
};
return await this.makeConcurrentCall({
processName: 'Update content types (removing mandatory references temporarily)',
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-utilities/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/cli-utilities",
"version": "1.14.1",
"version": "1.14.2",
"description": "Utilities for contentstack projects",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
23 changes: 16 additions & 7 deletions packages/contentstack-utilities/src/authentication-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cliux as ux, authHandler, configHandler } from './index';
import { cliux as ux, authHandler, configHandler, formatError } from './index';

class AuthenticationHandler {
private authType: string;
Expand Down Expand Up @@ -33,7 +33,8 @@ class AuthenticationHandler {
break;
}
} catch (error) {
ux.print(`Error occurred while fetching auth details: ${error?.message}`, {
const formattedError = formatError(error);
ux.print(`Error occurred while fetching auth details: ${formattedError}`, {
color: 'red',
});
throw error;
Expand Down Expand Up @@ -75,7 +76,9 @@ class AuthenticationHandler {
if (refreshed) {
return this.refreshAccessToken(error, maxRetryCount); // Retry after refreshing the token
}
console.log('API(401) case error:-', error.response);

const errorDetails = formatError(error);
ux.print(`Authentication failed: ${errorDetails}`, { color: 'red' });
// For Basic Auth, exit immediately without retrying
return;
}
Expand All @@ -84,9 +87,11 @@ class AuthenticationHandler {
case 429:
case 408:
if (maxRetryCount >= 3) {
ux.print(`Max retry count reached, please login to proceed, status code: ${error.response.status}`, {
color: 'yellow',
});
const errorDetails = formatError(error);
const statusText = error?.response?.status === 429 ? 'Rate Limited' : 'Request Timeout';
ux.print(`Max retry attempts exceeded (${maxRetryCount}/3)`, { color: 'red' });
ux.print(`Status: ${error?.response?.status} - ${statusText}`, { color: 'yellow' });
ux.print(`Error: ${errorDetails}`, { color: 'white' });
return;
}
maxRetryCount++; // Increment for the next retry attempt
Expand All @@ -106,6 +111,8 @@ class AuthenticationHandler {
ux.print('Session timed out, please login to proceed', {
color: 'yellow',
});
ux.print('\nTo fix this:', { color: 'cyan' });
ux.print('• Run: "csdx auth:login"', { color: 'white' });
resolve(false);
} else if (this.authType === 'OAUTH') {
authHandler.host = hostName;
Expand All @@ -117,7 +124,9 @@ class AuthenticationHandler {
resolve(true);
})
.catch((error: any) => {
console.log(error);
const errorDetails = formatError(error);
ux.print('OAuth Token Refresh Failed', { color: 'red' });
ux.print(`Error: ${errorDetails}`, { color: 'white' });
resolve(false);
});
} else {
Expand Down
57 changes: 30 additions & 27 deletions packages/contentstack-utilities/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,29 @@ export const formatError = function (error: any) {
parsedError = error;
}

// Helper function to append error details
const appendErrorDetails = (message: string, errorObj: any): string => {
if (errorObj.errors && typeof errorObj.errors === 'object' && Object.keys(errorObj.errors).length > 0) {
const entityNames: { [key: string]: string } = {
authorization: 'Authentication',
api_key: 'Stack API key',
uid: 'Content Type',
access_token: 'Delivery Token',
};

const errorList = Object.entries(errorObj.errors)
.map(([field, errors]) => {
const errorArray = Array.isArray(errors) ? errors : [errors];
const fieldName = entityNames[field] || field;
return ` • ${fieldName}: ${errorArray.join(', ')}`;
})
.join('\n');

return `${message}\n\nError Details:\n${errorList}\n`;
}
return message;
};

if (parsedError && typeof parsedError === 'object' && Object.keys(parsedError).length === 0) {
if (
!parsedError.message &&
Expand All @@ -121,25 +144,25 @@ export const formatError = function (error: any) {
}

if (parsedError?.response?.data?.errorMessage) {
return parsedError.response.data.errorMessage;
return appendErrorDetails(parsedError.response.data.errorMessage, parsedError?.response?.data || parsedError);
}

if (parsedError?.errorMessage) {
return parsedError.errorMessage;
return appendErrorDetails(parsedError.errorMessage, parsedError);
}

const status = parsedError?.status || parsedError?.response?.status;
const errorCode = parsedError?.errorCode || parsedError?.response?.data?.errorCode;
if (status === 422 && errorCode === 104) {
return 'Invalid email or password. Please check your credentials and try again.';
return appendErrorDetails('Invalid email or password. Please check your credentials and try again.', parsedError);
}

if (status === 401) {
return 'Authentication failed. Please check your credentials.';
return appendErrorDetails('Authentication failed. Please check your credentials.', parsedError);
}

if (status === 403) {
return 'Access denied. Please check your permissions.';
return appendErrorDetails('Access denied. Please check your permissions.', parsedError);
}

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

// Append detailed error information if available
if (parsedError.errors && typeof parsedError.errors === 'object' && Object.keys(parsedError.errors).length > 0) {
const entityNames: { [key: string]: string } = {
authorization: 'Authentication',
api_key: 'Stack API key',
uid: 'Content Type',
// 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.
access_token: 'Delivery Token',
};

const errorList = Object.entries(parsedError.errors)
.map(([field, errors]) => {
const errorArray = Array.isArray(errors) ? errors : [errors];
const fieldName = entityNames[field] || field;
return ` • ${fieldName}: ${errorArray.join(', ')}`;
})
.join('\n');

message += `\n\nAPI Errors:\n${errorList}`;
}

return message;
// Always append error details at the end
return appendErrorDetails(message, parsedError);
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,6 @@ export default class CLIErrorHandler {
* Extracts a clear, concise error message from various error types.
*/
private extractClearMessage(error: Error & Record<string, any>): string {
if (error?.response?.data?.errorMessage) {
return error.response.data.errorMessage;
}

if (error?.errorMessage) {
return error.errorMessage;
}

// Use existing formatError function for other cases
try {
const formattedMessage = formatError(error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
sanitizePath(entry_uid),
);

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

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

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

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